diff --git a/apps/docs/content/docs/integrations/airtable.mdx b/apps/docs/content/docs/integrations/airtable.mdx index 9957dfcc309..8ffd305edff 100644 --- a/apps/docs/content/docs/integrations/airtable.mdx +++ b/apps/docs/content/docs/integrations/airtable.mdx @@ -29,6 +29,17 @@ In Sim, the Airtable integration enables your agents to interact with your Airta Integrates Airtable into the workflow. Can list bases, list tables (with schema), and create, get, list, update, upsert, or delete records. Can also be used in trigger mode to trigger a workflow when an update is made to an Airtable table. +## Scopes + +Connecting Airtable through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `data.records:read` | Read records | +| `data.records:write` | Write to records | +| `schema.bases:read` | View bases and tables | +| `user.email:read` | Read email address | +| `webhook:manage` | Manage webhooks | ## Actions diff --git a/apps/docs/content/docs/integrations/asana.mdx b/apps/docs/content/docs/integrations/asana.mdx index 2813bae4502..694043b23cf 100644 --- a/apps/docs/content/docs/integrations/asana.mdx +++ b/apps/docs/content/docs/integrations/asana.mdx @@ -30,6 +30,13 @@ In Sim, the Asana integration enables your agents to programmatically interact w Integrate Asana into the workflow. Can read, write, and update tasks. +## Scopes + +Connecting Asana through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `default` | Access Asana workspace | ## Actions diff --git a/apps/docs/content/docs/integrations/attio.mdx b/apps/docs/content/docs/integrations/attio.mdx index 4e180bc9559..7ec4b5a4df0 100644 --- a/apps/docs/content/docs/integrations/attio.mdx +++ b/apps/docs/content/docs/integrations/attio.mdx @@ -30,6 +30,21 @@ In Sim, the Attio integration lets your agents programmatically query lists, fet Connect to Attio to manage CRM records (people, companies, custom objects), notes, tasks, lists, list entries, comments, workspace members, and webhooks. +## Scopes + +Connecting Attio through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `record_permission:read-write` | Read and write CRM records | +| `object_configuration:read-write` | Read and manage object schemas | +| `list_configuration:read-write` | Read and manage list configurations | +| `list_entry:read-write` | Read and write list entries | +| `note:read-write` | Read and write notes | +| `task:read-write` | Read and write tasks | +| `comment:read-write` | Read and write comments and threads | +| `user_management:read` | View workspace members | +| `webhook:read-write` | Manage webhooks | ## Actions diff --git a/apps/docs/content/docs/integrations/bitbucket.mdx b/apps/docs/content/docs/integrations/bitbucket.mdx index eba02b0e083..b8402dfc7be 100644 --- a/apps/docs/content/docs/integrations/bitbucket.mdx +++ b/apps/docs/content/docs/integrations/bitbucket.mdx @@ -15,6 +15,20 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" Connect Bitbucket Cloud to inspect repositories and source, collaborate on pull requests, diagnose or control pipelines, and start workflows from repository and pull request events. OAuth is used for actions and automatic webhook management. +## Scopes + +Connecting Bitbucket through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `account` | View your Bitbucket account and workspace memberships | +| `repository` | View repositories and source code | +| `repository:write` | Create and modify repositories, branches, and source code | +| `pullrequest` | View pull requests, comments, approvals, and statuses | +| `pullrequest:write` | Create, update, approve, decline, and merge pull requests | +| `pipeline` | View pipelines, steps, and logs | +| `pipeline:write` | Run and stop pipelines | +| `webhook` | Manage repository webhooks | ## Actions diff --git a/apps/docs/content/docs/integrations/box.mdx b/apps/docs/content/docs/integrations/box.mdx index 444761febcb..bea64fa9a09 100644 --- a/apps/docs/content/docs/integrations/box.mdx +++ b/apps/docs/content/docs/integrations/box.mdx @@ -39,6 +39,14 @@ These capabilities allow your Sim agents to automate Box operations directly wit Integrate Box into your workflow to manage files, folders, and e-signatures. Upload and download files, search content, create folders, send documents for e-signature, track signing status, and more. +## Scopes + +Connecting Box through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `root_readwrite` | Read and write all files and folders in Box account | +| `sign_requests.readwrite` | Create and manage Box Sign e-signature requests | ## Actions diff --git a/apps/docs/content/docs/integrations/confluence.mdx b/apps/docs/content/docs/integrations/confluence.mdx index 62398bc3491..194268c1b59 100644 --- a/apps/docs/content/docs/integrations/confluence.mdx +++ b/apps/docs/content/docs/integrations/confluence.mdx @@ -30,6 +30,48 @@ In Sim, the Confluence integration enables your agents to access and leverage yo Integrate Confluence into the workflow. Can read, create, update, delete pages, manage comments, attachments, labels, and search content. +## Scopes + +Connecting Confluence through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `read:confluence-content.all` | Read all Confluence content | +| `read:confluence-space.summary` | Read Confluence space information | +| `read:space:confluence` | View Confluence spaces | +| `write:confluence-content` | Create and edit Confluence pages | +| `write:confluence-space` | Manage Confluence spaces | +| `write:confluence-file` | Upload files to Confluence | +| `read:page:confluence` | View Confluence pages | +| `write:page:confluence` | Create and update Confluence pages | +| `read:comment:confluence` | View comments on Confluence pages | +| `write:comment:confluence` | Create and update comments | +| `delete:comment:confluence` | Delete comments from Confluence pages | +| `delete:attachment:confluence` | Delete attachments from Confluence pages | +| `delete:page:confluence` | Delete Confluence pages | +| `read:label:confluence` | View labels on Confluence content | +| `write:label:confluence` | Add and remove labels | +| `read:attachment:confluence` | View attachments on Confluence pages | +| `write:attachment:confluence` | Upload and manage attachments | +| `search:confluence` | Search Confluence content | +| `read:me` | Read profile information | +| `offline_access` | Access account when not using the application | +| `read:hierarchical-content:confluence` | View page hierarchy \(children and ancestors\) | +| `read:content.metadata:confluence` | View content metadata \(required for ancestors\) | +| `read:user:confluence` | View Confluence user profiles | +| `read:confluence-user` | View Confluence user profiles \(v1 API\) | +| `read:task:confluence` | View Confluence inline tasks | +| `write:task:confluence` | Update Confluence inline tasks | +| `write:space:confluence` | Create and update Confluence spaces | +| `delete:space:confluence` | Delete Confluence spaces | +| `read:blogpost:confluence` | View Confluence blog posts | +| `write:blogpost:confluence` | Create and update Confluence blog posts | +| `delete:blogpost:confluence` | Delete Confluence blog posts | +| `read:content.property:confluence` | View properties on Confluence content | +| `write:content.property:confluence` | Create and manage content properties | +| `read:space.property:confluence` | View Confluence space properties | +| `write:space.property:confluence` | Create and manage space properties | +| `read:space.permission:confluence` | View Confluence space permissions | ## Actions diff --git a/apps/docs/content/docs/integrations/docusign.mdx b/apps/docs/content/docs/integrations/docusign.mdx index d2029d33f83..b04b8385edf 100644 --- a/apps/docs/content/docs/integrations/docusign.mdx +++ b/apps/docs/content/docs/integrations/docusign.mdx @@ -32,6 +32,14 @@ In Sim, the DocuSign integration enables your agents to automate document workfl Create and send envelopes for e-signature, use templates, check signing status, download signed documents, and manage recipients with DocuSign. +## Scopes + +Connecting DocuSign through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `signature` | Create and send envelopes for e-signature | +| `extended` | Extended access to DocuSign account features | ## Actions diff --git a/apps/docs/content/docs/integrations/dropbox.mdx b/apps/docs/content/docs/integrations/dropbox.mdx index 27b95131829..a8691da5442 100644 --- a/apps/docs/content/docs/integrations/dropbox.mdx +++ b/apps/docs/content/docs/integrations/dropbox.mdx @@ -31,6 +31,19 @@ These capabilities allow your Sim agents to automate Dropbox operations directly Integrate Dropbox into your workflow for file management, sharing, and collaboration. Upload files, download content, create folders, manage shared links, and more. +## Scopes + +Connecting Dropbox through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `account_info.read` | View Dropbox account information | +| `files.metadata.read` | View file and folder names, sizes, and dates | +| `files.metadata.write` | Modify file and folder metadata | +| `files.content.read` | Download and read Dropbox files | +| `files.content.write` | Upload, copy, move, and delete files in Dropbox | +| `sharing.read` | View shared files and folders | +| `sharing.write` | Share files and folders with others | ## Actions diff --git a/apps/docs/content/docs/integrations/gmail.mdx b/apps/docs/content/docs/integrations/gmail.mdx index 55f2b5d69f6..0cbf7ae1d3a 100644 --- a/apps/docs/content/docs/integrations/gmail.mdx +++ b/apps/docs/content/docs/integrations/gmail.mdx @@ -34,6 +34,19 @@ In Sim, the Gmail integration enables your agents to interact with your inbox pr Integrate Gmail into the workflow. Can send, read, search, and move emails. Can be used in trigger mode to trigger a workflow when a new email is received. +## Scopes + +Connecting Gmail through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/gmail.send` | Send emails | +| `https://www.googleapis.com/auth/gmail.modify` | View and manage email messages | +| `https://www.googleapis.com/auth/gmail.labels` | View and manage email labels | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_ads.mdx b/apps/docs/content/docs/integrations/google_ads.mdx index a99a7f7bb3b..60d1f9a5301 100644 --- a/apps/docs/content/docs/integrations/google_ads.mdx +++ b/apps/docs/content/docs/integrations/google_ads.mdx @@ -22,6 +22,15 @@ In Sim, the Google Ads integration enables your agents to query campaign data, m Connect to Google Ads to list accessible accounts, list campaigns, view ad group details, get performance metrics, and run custom GAQL queries. +## Scopes + +Connecting Google Ads through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/adwords` | Manage Google Ads campaigns and reporting | ## Actions diff --git a/apps/docs/content/docs/integrations/google_bigquery.mdx b/apps/docs/content/docs/integrations/google_bigquery.mdx index 778a514ac90..7f5040dad05 100644 --- a/apps/docs/content/docs/integrations/google_bigquery.mdx +++ b/apps/docs/content/docs/integrations/google_bigquery.mdx @@ -29,6 +29,15 @@ In Sim, the Google BigQuery integration enables your agents to query datasets, i Connect to Google BigQuery to run SQL queries, list datasets and tables, get table metadata, and insert rows. +## Scopes + +Connecting Google BigQuery through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/bigquery` | View and manage data in Google BigQuery | ## Actions diff --git a/apps/docs/content/docs/integrations/google_calendar.mdx b/apps/docs/content/docs/integrations/google_calendar.mdx index 43fc5aa5b90..dd4190a65ed 100644 --- a/apps/docs/content/docs/integrations/google_calendar.mdx +++ b/apps/docs/content/docs/integrations/google_calendar.mdx @@ -33,6 +33,17 @@ In Sim, the Google Calendar integration allows your agents to read, create, upda Integrate Google Calendar into the workflow. Can create, read, update, and list calendar events. +## Scopes + +Connecting Google Calendar through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/calendar` | View and manage calendar | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_contacts.mdx b/apps/docs/content/docs/integrations/google_contacts.mdx index bb54b76b206..9f3368ed0d3 100644 --- a/apps/docs/content/docs/integrations/google_contacts.mdx +++ b/apps/docs/content/docs/integrations/google_contacts.mdx @@ -28,6 +28,15 @@ In Sim, the Google Contacts integration allows your agents to create, read, upda Integrate Google Contacts into the workflow. Can create, read, update, delete, list, and search contacts. +## Scopes + +Connecting Google Contacts through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/contacts` | View and manage Google Contacts | ## Actions diff --git a/apps/docs/content/docs/integrations/google_docs.mdx b/apps/docs/content/docs/integrations/google_docs.mdx index 93a1c652ef0..0a0bd00fb3a 100644 --- a/apps/docs/content/docs/integrations/google_docs.mdx +++ b/apps/docs/content/docs/integrations/google_docs.mdx @@ -30,6 +30,18 @@ In Sim, the Google Docs integration allows your agents to read document content, Integrate Google Docs into the workflow. Read, write, and create documents, insert text, tables, images, and page breaks, find and replace text, and apply text styling. +## Scopes + +Connecting Google Docs through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/drive.file` | View and manage Google Drive files | +| `https://www.googleapis.com/auth/drive` | Access all Google Drive files | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_drive.mdx b/apps/docs/content/docs/integrations/google_drive.mdx index 62db54a219a..678cb7d391d 100644 --- a/apps/docs/content/docs/integrations/google_drive.mdx +++ b/apps/docs/content/docs/integrations/google_drive.mdx @@ -31,6 +31,18 @@ In Sim, the Google Drive integration allows your agents to read, upload, downloa Integrate Google Drive into the workflow. Can create, upload, download, copy, move, delete, share files and manage permissions. +## Scopes + +Connecting Google Drive through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/drive.file` | View and manage Google Drive files | +| `https://www.googleapis.com/auth/drive` | Access all Google Drive files | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_forms.mdx b/apps/docs/content/docs/integrations/google_forms.mdx index f92423bfabc..b6d68912511 100644 --- a/apps/docs/content/docs/integrations/google_forms.mdx +++ b/apps/docs/content/docs/integrations/google_forms.mdx @@ -32,6 +32,19 @@ In Sim, the Google Forms integration enables your agents to programmatically acc Integrate Google Forms into your workflow. Read form structure, get responses, create forms, update content, and manage notification watches. +## Scopes + +Connecting Google Forms through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/drive` | Access all Google Drive files | +| `https://www.googleapis.com/auth/forms.body` | View and manage Google Forms | +| `https://www.googleapis.com/auth/forms.responses.readonly` | View responses to Google Forms | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_groups.mdx b/apps/docs/content/docs/integrations/google_groups.mdx index db54d6c0961..803b29d13f7 100644 --- a/apps/docs/content/docs/integrations/google_groups.mdx +++ b/apps/docs/content/docs/integrations/google_groups.mdx @@ -32,6 +32,16 @@ In Sim, the Google Groups integration gives your agents API-driven control to au Connect to Google Workspace to create, update, and manage groups and their members using the Admin SDK Directory API. +## Scopes + +Connecting Google Groups through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/admin.directory.group` | Manage Google Workspace groups | +| `https://www.googleapis.com/auth/admin.directory.group.member` | Manage Google Workspace group memberships | ## Actions diff --git a/apps/docs/content/docs/integrations/google_meet.mdx b/apps/docs/content/docs/integrations/google_meet.mdx index a6da5e7c27d..0d717f68424 100644 --- a/apps/docs/content/docs/integrations/google_meet.mdx +++ b/apps/docs/content/docs/integrations/google_meet.mdx @@ -32,6 +32,18 @@ In Sim, the Google Meet integration allows your agents to create meeting spaces Integrate Google Meet into your workflow. Create meeting spaces, get space details, end conferences, list conference records, and view participants. +## Scopes + +Connecting Google Meet through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/meetings.space.created` | Create Google Meet spaces and manage the ones created through Sim | +| `https://www.googleapis.com/auth/meetings.space.readonly` | View Google Meet meeting space details | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_sheets.mdx b/apps/docs/content/docs/integrations/google_sheets.mdx index b6602c62a3e..7fd2c2a8be7 100644 --- a/apps/docs/content/docs/integrations/google_sheets.mdx +++ b/apps/docs/content/docs/integrations/google_sheets.mdx @@ -33,6 +33,18 @@ In Sim, the Google Sheets integration enables your agents to read from, write to Integrate Google Sheets into the workflow with explicit sheet selection. Can read, write, append, update, clear data, create spreadsheets, get spreadsheet info, and copy sheets. +## Scopes + +Connecting Google Sheets through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/drive.file` | View and manage Google Drive files | +| `https://www.googleapis.com/auth/drive` | Access all Google Drive files | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_slides.mdx b/apps/docs/content/docs/integrations/google_slides.mdx index 40e5064b18a..f5ec797c7af 100644 --- a/apps/docs/content/docs/integrations/google_slides.mdx +++ b/apps/docs/content/docs/integrations/google_slides.mdx @@ -33,6 +33,18 @@ In Sim, the Google Slides integration enables your agents to interact directly w Build, edit, and export branded Google Slides presentations end-to-end. Copy a template, replace text and image tokens, embed Sheets charts, style text and shapes with brand fonts and colors, manage tables and layouts, group elements, run atomic batch updates, and export to PDF or PPTX. +## Scopes + +Connecting Google Slides through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/drive.file` | View and manage Google Drive files | +| `https://www.googleapis.com/auth/drive` | Access all Google Drive files | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. ## Actions diff --git a/apps/docs/content/docs/integrations/google_tasks.mdx b/apps/docs/content/docs/integrations/google_tasks.mdx index 67e02977461..6db53376462 100644 --- a/apps/docs/content/docs/integrations/google_tasks.mdx +++ b/apps/docs/content/docs/integrations/google_tasks.mdx @@ -31,6 +31,15 @@ In Sim, the Google Tasks integration allows your agents to manage to-do items pr Integrate Google Tasks into your workflow. Create, read, update, delete, and list tasks and task lists. +## Scopes + +Connecting Google Tasks through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/tasks` | Create, read, update, and delete Google Tasks | ## Actions diff --git a/apps/docs/content/docs/integrations/google_vault.mdx b/apps/docs/content/docs/integrations/google_vault.mdx index 60ea81d70a9..db8bcd0caa1 100644 --- a/apps/docs/content/docs/integrations/google_vault.mdx +++ b/apps/docs/content/docs/integrations/google_vault.mdx @@ -31,6 +31,17 @@ In Sim, the Google Vault integration lets your AI agents programmatically manage Connect Google Vault to manage the full matter lifecycle, create and manage holds and exports, and save reusable search queries for eDiscovery and compliance. +## Scopes + +Connecting Google Vault through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `https://www.googleapis.com/auth/userinfo.email` | View email address | +| `https://www.googleapis.com/auth/userinfo.profile` | View basic profile info | +| `https://www.googleapis.com/auth/ediscovery` | Access Google Vault for eDiscovery | +| `https://www.googleapis.com/auth/ediscovery.readonly` | View Google Vault matters, holds, and saved queries | +| `https://www.googleapis.com/auth/devstorage.read_only` | Read files from Google Cloud Storage | ## Actions diff --git a/apps/docs/content/docs/integrations/hubspot.mdx b/apps/docs/content/docs/integrations/hubspot.mdx index 52b830714ab..d729926fead 100644 --- a/apps/docs/content/docs/integrations/hubspot.mdx +++ b/apps/docs/content/docs/integrations/hubspot.mdx @@ -31,6 +31,32 @@ New to the integration? Follow the [HubSpot setup guide](/integrations/hubspot-s Integrate HubSpot into your workflow. Manage contacts, companies, deals, tickets, and other CRM objects with powerful automation capabilities. Can be used in trigger mode to start workflows when records are created, updated, a specific property changes, or a contact joins a list. +## Scopes + +Connecting HubSpot through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `crm.objects.contacts.read` | Read HubSpot contacts | +| `crm.objects.contacts.write` | Create and update HubSpot contacts | +| `crm.objects.companies.read` | Read HubSpot companies | +| `crm.objects.companies.write` | Create and update HubSpot companies | +| `crm.objects.deals.read` | Read HubSpot deals | +| `crm.objects.deals.write` | Create and update HubSpot deals | +| `crm.objects.owners.read` | Read HubSpot object owners | +| `crm.objects.users.read` | Read HubSpot users | +| `crm.objects.marketing_events.read` | Read HubSpot marketing events | +| `crm.objects.line_items.read` | Read HubSpot line items | +| `crm.objects.line_items.write` | Create and update HubSpot line items | +| `crm.objects.quotes.read` | Read HubSpot quotes | +| `crm.objects.appointments.read` | Read HubSpot appointments | +| `crm.objects.appointments.write` | Create and update HubSpot appointments | +| `crm.objects.carts.read` | Read HubSpot shopping carts | +| `sales-email-read` | Read the content of HubSpot email engagements | +| `crm.lists.read` | Read HubSpot lists | +| `crm.lists.write` | Create and update HubSpot lists | +| `tickets` | Access HubSpot tickets | +| `oauth` | Authenticate with HubSpot OAuth | ## Actions diff --git a/apps/docs/content/docs/integrations/jira.mdx b/apps/docs/content/docs/integrations/jira.mdx index 818ab031c6f..0ba042c27be 100644 --- a/apps/docs/content/docs/integrations/jira.mdx +++ b/apps/docs/content/docs/integrations/jira.mdx @@ -35,6 +35,53 @@ In Sim, the Jira integration enables your agents to interact with your project m Integrate Jira into the workflow. Can read, write, and update issues. Can also trigger workflows based on Jira webhook events. +## Scopes + +Connecting Jira through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `read:jira-user` | Read Jira user | +| `read:jira-work` | Read Jira work | +| `write:jira-work` | Write to Jira work | +| `read:me` | Read profile information | +| `offline_access` | Access account when not using the application | +| `read:issue.vote:jira` | Read Jira issue votes | +| `read:user:jira` | Read Jira user | +| `delete:issue:jira` | Delete Jira issues | +| `delete:comment:jira` | Delete comments from Jira issues | +| `delete:attachment:jira` | Delete attachments from Jira issues | +| `delete:issue-worklog:jira` | Delete worklog entries from Jira issues | +| `delete:issue-link:jira` | Delete links between Jira issues | +| `read:servicedesk-request` | View service desk requests | +| `write:servicedesk-request` | Create and update service desk requests | +| `manage:servicedesk-customer` | Manage service desk customers and organizations | +| `read:servicedesk:jira-service-management` | View service desks and their settings | +| `read:requesttype:jira-service-management` | View request types available in service desks | +| `read:request:jira-service-management` | View customer requests in service desks | +| `write:request:jira-service-management` | Create customer requests in service desks | +| `read:request.comment:jira-service-management` | View comments on customer requests | +| `write:request.comment:jira-service-management` | Add comments to customer requests | +| `read:servicedesk.customer:jira-service-management` | View customers linked to service desks | +| `write:servicedesk.customer:jira-service-management` | Add and remove customers from service desks | +| `read:organization:jira-service-management` | View organizations | +| `write:organization:jira-service-management` | Create and manage organizations | +| `read:servicedesk.organization:jira-service-management` | View organizations linked to service desks | +| `write:servicedesk.organization:jira-service-management` | Add and remove organizations from service desks | +| `read:queue:jira-service-management` | View service desk queues and their issues | +| `read:request.sla:jira-service-management` | View SLA information for customer requests | +| `read:request.status:jira-service-management` | View status of customer requests | +| `write:request.status:jira-service-management` | Transition customer request status | +| `read:request.participant:jira-service-management` | View participants on customer requests | +| `write:request.participant:jira-service-management` | Add and remove participants from customer requests | +| `read:request.approval:jira-service-management` | View approvals on customer requests | +| `write:request.approval:jira-service-management` | Approve or decline customer requests | +| `read:cmdb-object:jira` | View Assets objects and run AQL searches | +| `write:cmdb-object:jira` | Create and update Assets objects | +| `delete:cmdb-object:jira` | Delete Assets objects | +| `read:cmdb-schema:jira` | View Assets object schemas | +| `read:cmdb-type:jira` | View Assets object types | +| `read:cmdb-attribute:jira` | View Assets object type attributes | ## Actions diff --git a/apps/docs/content/docs/integrations/jira_service_management.mdx b/apps/docs/content/docs/integrations/jira_service_management.mdx index 87dcc4094db..10eaacebe89 100644 --- a/apps/docs/content/docs/integrations/jira_service_management.mdx +++ b/apps/docs/content/docs/integrations/jira_service_management.mdx @@ -33,6 +33,53 @@ With Sim’s Jira Service Management integration, you can create, monitor, and u Integrate with Jira Service Management for IT service management. Create and manage service requests, handle customers and organizations, track SLAs, and manage queues. +## Scopes + +Connecting Jira Service Management through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `read:jira-user` | Read Jira user | +| `read:jira-work` | Read Jira work | +| `write:jira-work` | Write to Jira work | +| `read:me` | Read profile information | +| `offline_access` | Access account when not using the application | +| `read:issue.vote:jira` | Read Jira issue votes | +| `read:user:jira` | Read Jira user | +| `delete:issue:jira` | Delete Jira issues | +| `delete:comment:jira` | Delete comments from Jira issues | +| `delete:attachment:jira` | Delete attachments from Jira issues | +| `delete:issue-worklog:jira` | Delete worklog entries from Jira issues | +| `delete:issue-link:jira` | Delete links between Jira issues | +| `read:servicedesk-request` | View service desk requests | +| `write:servicedesk-request` | Create and update service desk requests | +| `manage:servicedesk-customer` | Manage service desk customers and organizations | +| `read:servicedesk:jira-service-management` | View service desks and their settings | +| `read:requesttype:jira-service-management` | View request types available in service desks | +| `read:request:jira-service-management` | View customer requests in service desks | +| `write:request:jira-service-management` | Create customer requests in service desks | +| `read:request.comment:jira-service-management` | View comments on customer requests | +| `write:request.comment:jira-service-management` | Add comments to customer requests | +| `read:servicedesk.customer:jira-service-management` | View customers linked to service desks | +| `write:servicedesk.customer:jira-service-management` | Add and remove customers from service desks | +| `read:organization:jira-service-management` | View organizations | +| `write:organization:jira-service-management` | Create and manage organizations | +| `read:servicedesk.organization:jira-service-management` | View organizations linked to service desks | +| `write:servicedesk.organization:jira-service-management` | Add and remove organizations from service desks | +| `read:queue:jira-service-management` | View service desk queues and their issues | +| `read:request.sla:jira-service-management` | View SLA information for customer requests | +| `read:request.status:jira-service-management` | View status of customer requests | +| `write:request.status:jira-service-management` | Transition customer request status | +| `read:request.participant:jira-service-management` | View participants on customer requests | +| `write:request.participant:jira-service-management` | Add and remove participants from customer requests | +| `read:request.approval:jira-service-management` | View approvals on customer requests | +| `write:request.approval:jira-service-management` | Approve or decline customer requests | +| `read:cmdb-object:jira` | View Assets objects and run AQL searches | +| `write:cmdb-object:jira` | Create and update Assets objects | +| `delete:cmdb-object:jira` | Delete Assets objects | +| `read:cmdb-schema:jira` | View Assets object schemas | +| `read:cmdb-type:jira` | View Assets object types | +| `read:cmdb-attribute:jira` | View Assets object type attributes | ## Actions diff --git a/apps/docs/content/docs/integrations/linear.mdx b/apps/docs/content/docs/integrations/linear.mdx index 37b8f918902..14c88087509 100644 --- a/apps/docs/content/docs/integrations/linear.mdx +++ b/apps/docs/content/docs/integrations/linear.mdx @@ -34,6 +34,14 @@ In Sim, the Linear integration enables your agents to interact with your project Integrate Linear into the workflow. Can manage issues, comments, projects, labels, workflow states, cycles, attachments, and more. Can also trigger workflows based on Linear webhook events. +## Scopes + +Connecting Linear through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `read` | Read access to connected account data | +| `write` | Write access to connected account data | ## Actions diff --git a/apps/docs/content/docs/integrations/linkedin.mdx b/apps/docs/content/docs/integrations/linkedin.mdx index 9e1c6164e1e..aa7c8e0fdeb 100644 --- a/apps/docs/content/docs/integrations/linkedin.mdx +++ b/apps/docs/content/docs/integrations/linkedin.mdx @@ -29,6 +29,16 @@ These capabilities make it easy to keep your LinkedIn network engaged and to ext Integrate LinkedIn into workflows. Share posts to your personal feed and access your LinkedIn profile information. +## Scopes + +Connecting LinkedIn through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `profile` | Access profile information | +| `openid` | Standard authentication | +| `email` | Access email address | +| `w_member_social` | Post, comment, and like posts on your behalf | ## Actions diff --git a/apps/docs/content/docs/integrations/manageengine_sdp.mdx b/apps/docs/content/docs/integrations/manageengine_sdp.mdx index e89d1cb955e..079208c4200 100644 --- a/apps/docs/content/docs/integrations/manageengine_sdp.mdx +++ b/apps/docs/content/docs/integrations/manageengine_sdp.mdx @@ -57,6 +57,33 @@ Three things shape what will work: Full read and write access to ManageEngine ServiceDesk Plus Cloud: create, search, update and delete requests, problems, changes, assets and knowledge base solutions, and add notes to requests, problems and changes. Supports multi-portal accounts. Connecting requires a Zoho account in the US data center. +## Scopes + +Connecting ManageEngine ServiceDesk Plus through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `SDPOnDemand.requests.CREATE` | Create requests | +| `SDPOnDemand.requests.READ` | View requests and their notes | +| `SDPOnDemand.requests.UPDATE` | Update requests and add notes | +| `SDPOnDemand.requests.DELETE` | Delete requests | +| `SDPOnDemand.problems.CREATE` | Create problems | +| `SDPOnDemand.problems.READ` | View problems and their notes | +| `SDPOnDemand.problems.UPDATE` | Update problems and add notes | +| `SDPOnDemand.problems.DELETE` | Delete problems | +| `SDPOnDemand.changes.CREATE` | Create changes | +| `SDPOnDemand.changes.READ` | View changes and their notes | +| `SDPOnDemand.changes.UPDATE` | Update changes and add notes | +| `SDPOnDemand.changes.DELETE` | Delete changes | +| `SDPOnDemand.assets.CREATE` | Create assets | +| `SDPOnDemand.assets.READ` | View assets | +| `SDPOnDemand.assets.UPDATE` | Update assets | +| `SDPOnDemand.assets.DELETE` | Delete assets | +| `SDPOnDemand.solutions.CREATE` | Create knowledge base solutions | +| `SDPOnDemand.solutions.READ` | View knowledge base solutions | +| `SDPOnDemand.solutions.UPDATE` | Update knowledge base solutions | +| `SDPOnDemand.solutions.DELETE` | Delete knowledge base solutions | +| `aaaserver.profile.READ` | View your Zoho profile | ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_ad.mdx b/apps/docs/content/docs/integrations/microsoft_ad.mdx index 3fff9ba8934..0a45b96ecfc 100644 --- a/apps/docs/content/docs/integrations/microsoft_ad.mdx +++ b/apps/docs/content/docs/integrations/microsoft_ad.mdx @@ -34,6 +34,28 @@ If you encounter issues with the Azure AD integration, contact us at [help@sim.a Integrate Azure Active Directory into your workflows. Create, update, and delete users and groups, manage group memberships, assign and remove licenses, reset passwords, revoke sign-in sessions, read sign-in and directory audit logs, grant and revoke app and directory roles, and read registered devices and conditional access policies. Device writes are not supported. +## Scopes + +Connecting Azure AD through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `User.ReadWrite.All` | Read and write all user profiles | +| `Group.ReadWrite.All` | Read and write all groups | +| `GroupMember.ReadWrite.All` | Read and write all group memberships | +| `LicenseAssignment.Read.All` | Read license assignments and subscribed SKUs | +| `LicenseAssignment.ReadWrite.All` | Read, assign, and remove user licenses | +| `UserAuthenticationMethod.ReadWrite.All` | Read and reset authentication methods and passwords for all users | +| `AuditLog.Read.All` | Read sign-in and directory audit logs | +| `Application.Read.All` | Read all applications and service principals | +| `AppRoleAssignment.ReadWrite.All` | Read, grant, and revoke application role assignments | +| `RoleManagement.ReadWrite.Directory` | Read and manage directory role assignments | +| `Device.Read.All` | Read all devices | +| `Policy.Read.All` | Read conditional access and other policies | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_dataverse.mdx b/apps/docs/content/docs/integrations/microsoft_dataverse.mdx index 03cbab8f6ea..3da842ac6c6 100644 --- a/apps/docs/content/docs/integrations/microsoft_dataverse.mdx +++ b/apps/docs/content/docs/integrations/microsoft_dataverse.mdx @@ -33,6 +33,19 @@ Connect Microsoft Dataverse to your automations to unlock sophisticated data man Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, query, search, and execute actions and functions against Dataverse tables using the Web API. Supports bulk operations, FetchXML, file uploads, relevance search, and table metadata lookup. Works with Dynamics 365, Power Platform, and custom Dataverse environments. +## Scopes + +Connecting Microsoft Dataverse through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `https://dynamics.microsoft.com/user_impersonation` | Access Microsoft Dataverse on your behalf | +| `offline_access` | Access account when not using the application | + +Connecting a specific environment requests that environment's `/.default` scope instead of the resource scope above. The host is normalized to its `.api` form first, so `https://contoso.crm.dynamics.com` is requested as `https://contoso.api.crm.dynamics.com/.default`. ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_dynamics_365.mdx b/apps/docs/content/docs/integrations/microsoft_dynamics_365.mdx index 2d370cbe787..ba353982769 100644 --- a/apps/docs/content/docs/integrations/microsoft_dynamics_365.mdx +++ b/apps/docs/content/docs/integrations/microsoft_dynamics_365.mdx @@ -27,6 +27,19 @@ Connect one credential per Dynamics environment. The credential is bound to that Manage standard Microsoft Dynamics 365 CRM records through the Dataverse Web API. List, search, create, retrieve, and update accounts, contacts, leads, opportunities, and cases; assign records to users or teams; qualify leads; close opportunities; and resolve cases. Connect a separate Microsoft credential for each environment from this Dynamics integration page or from its workflow block; existing generic Dataverse credentials remain unchanged and are not automatically rebound. This version supports public-cloud Dynamics environments; national clouds require separate OAuth authorities. Dataverse Search must be enabled for search, and lifecycle actions require the corresponding Dynamics 365 app, security role, and record privileges. +## Scopes + +Connecting Microsoft Dynamics 365 CRM through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `https://dynamics.microsoft.com/user_impersonation` | Access Microsoft Dataverse on your behalf | +| `offline_access` | Access account when not using the application | + +Connecting a specific environment requests that environment's `/.default` scope instead of the resource scope above. The host is normalized to its `.api` form first, so `https://contoso.crm.dynamics.com` is requested as `https://contoso.api.crm.dynamics.com/.default`. ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_excel.mdx b/apps/docs/content/docs/integrations/microsoft_excel.mdx index ef12b24a054..68c253871ef 100644 --- a/apps/docs/content/docs/integrations/microsoft_excel.mdx +++ b/apps/docs/content/docs/integrations/microsoft_excel.mdx @@ -33,6 +33,21 @@ Connect Microsoft Excel to your automations to streamline data management, repor Integrate Microsoft Excel into the workflow with explicit sheet selection. Can read and write data in specific sheets. +## Scopes + +Connecting Microsoft Excel through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `Files.Read` | Read your workbooks in OneDrive | +| `Files.ReadWrite` | Read, create, and edit your workbooks in OneDrive | +| `Files.Read.All` | Read workbooks shared with you, including SharePoint libraries | +| `Files.ReadWrite.All` | Read, create, and edit workbooks you have access to, including SharePoint libraries | +| `Sites.Read.All` | List the SharePoint sites and document libraries you can open | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_planner.mdx b/apps/docs/content/docs/integrations/microsoft_planner.mdx index 6a2233fddf4..befd51d0bea 100644 --- a/apps/docs/content/docs/integrations/microsoft_planner.mdx +++ b/apps/docs/content/docs/integrations/microsoft_planner.mdx @@ -29,6 +29,19 @@ In Sim, the Microsoft Planner integration allows your agents to programmatically Integrate Microsoft Planner into the workflow. Manage tasks, plans, buckets, and task details including checklists and references. +## Scopes + +Connecting Microsoft Planner through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `Group.ReadWrite.All` | Read and write all groups | +| `Group.Read.All` | Read Microsoft groups | +| `Tasks.ReadWrite` | Read and manage Planner tasks | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_teams.mdx b/apps/docs/content/docs/integrations/microsoft_teams.mdx index 0bb6afdd969..1420208ad17 100644 --- a/apps/docs/content/docs/integrations/microsoft_teams.mdx +++ b/apps/docs/content/docs/integrations/microsoft_teams.mdx @@ -32,6 +32,32 @@ In Sim, the Microsoft Teams integration enables your agents to interact directly Integrate Microsoft Teams into the workflow. Read, write, update, and delete chat and channel messages. Reply to messages, add reactions, and list teams, chats, channels, and their members. Can be used in trigger mode to trigger a workflow when a message is sent to a chat or channel. To mention users in messages, wrap their name in `` tags: `userName` +## Scopes + +Connecting Microsoft Teams through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `User.Read` | Read Microsoft user | +| `Chat.Read` | Read Microsoft chats | +| `Chat.ReadWrite` | Read and write Microsoft chats | +| `Chat.ReadBasic` | Read Microsoft chats | +| `ChatMessage.Send` | Send chat messages | +| `Channel.ReadBasic.All` | Read Microsoft channels | +| `ChannelMessage.Send` | Send channel messages | +| `ChannelMessage.Read.All` | Read Microsoft channels | +| `ChannelMessage.ReadWrite` | Read and write to Microsoft channels | +| `ChannelMember.Read.All` | Read team channel members | +| `Group.Read.All` | Read Microsoft groups | +| `Group.ReadWrite.All` | Read and write all groups | +| `Team.ReadBasic.All` | Read Microsoft teams | +| `TeamMember.Read.All` | Read team members | +| `offline_access` | Access account when not using the application | +| `Files.Read` | Read OneDrive files | +| `Sites.Read.All` | Read SharePoint sites | ## Actions diff --git a/apps/docs/content/docs/integrations/microsoft_word.mdx b/apps/docs/content/docs/integrations/microsoft_word.mdx index ba24ca826ef..bed9d2a900f 100644 --- a/apps/docs/content/docs/integrations/microsoft_word.mdx +++ b/apps/docs/content/docs/integrations/microsoft_word.mdx @@ -65,7 +65,7 @@ Word splits a sentence across several internal runs whenever formatting changes ### Permissions -Connecting a Microsoft account grants Sim `Files.Read` and `Files.ReadWrite`, the permissions Microsoft Graph requires to read and write drive items. The same permissions cover SharePoint document libraries the account can already reach — Sim never gains access to anything the signed-in account could not open itself. +Connecting a Microsoft account grants Sim the `Files` permissions Microsoft Graph requires to read and write drive items, listed in full under [Scopes](#scopes). `Files.Read` and `Files.ReadWrite` cover your own OneDrive; the `.All` variants are what make a SharePoint document library work, because the basic pair reaches only the signed-in user's own drive. Sim never gains access to anything the signed-in account could not open itself. {/* MANUAL-CONTENT-END */} @@ -74,6 +74,20 @@ Connecting a Microsoft account grants Sim `Files.Read` and `Files.ReadWrite`, th Integrate Microsoft Word into the workflow. Create .docx documents from text, fill a formatted template by substituting its placeholders, read a document back as text, replace or append content, find and replace text across the body plus headers and footers, list and search documents in OneDrive or SharePoint, and export a document as PDF. +## Scopes + +Connecting Microsoft Word through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `Files.Read` | Read your Word documents in OneDrive | +| `Files.ReadWrite` | Read, create, and edit your Word documents in OneDrive | +| `Files.Read.All` | Read Word documents shared with you, including SharePoint libraries | +| `Files.ReadWrite.All` | Read, create, and edit Word documents you have access to, including SharePoint libraries | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/monday.mdx b/apps/docs/content/docs/integrations/monday.mdx index 06b0960ff46..cf0a48f9a62 100644 --- a/apps/docs/content/docs/integrations/monday.mdx +++ b/apps/docs/content/docs/integrations/monday.mdx @@ -29,6 +29,19 @@ In Sim, the Monday.com integration allows your agents to list and inspect boards Integrate with Monday.com to list boards, get board details, fetch and search items, create and update items, archive or delete items, create subitems, move items between groups, add updates, and create groups. +## Scopes + +Connecting Monday through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `boards:read` | Read boards, items, and columns | +| `boards:write` | Create and modify boards, items, and groups | +| `updates:read` | Read updates and comments | +| `updates:write` | Create and edit updates and comments | +| `webhooks:read` | Read webhook subscriptions | +| `webhooks:write` | Create and manage webhook subscriptions | +| `me:read` | Read your user profile | ## Actions diff --git a/apps/docs/content/docs/integrations/onedrive.mdx b/apps/docs/content/docs/integrations/onedrive.mdx index 934a7656c9a..3e4eb1d1461 100644 --- a/apps/docs/content/docs/integrations/onedrive.mdx +++ b/apps/docs/content/docs/integrations/onedrive.mdx @@ -33,6 +33,18 @@ In Sim, the OneDrive integration enables your agents to directly interact with y Integrate OneDrive into the workflow. Can create text and Excel files, upload files, download files, list and search files, move or rename files, copy files, create sharing links, and delete files or folders. +## Scopes + +Connecting OneDrive through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `Files.Read` | Read OneDrive files | +| `Files.ReadWrite` | Read and write OneDrive files | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/outlook.mdx b/apps/docs/content/docs/integrations/outlook.mdx index a797f6c9673..bf8136bfc9f 100644 --- a/apps/docs/content/docs/integrations/outlook.mdx +++ b/apps/docs/content/docs/integrations/outlook.mdx @@ -40,6 +40,21 @@ By connecting Sim with Microsoft Outlook, you enable intelligent agents to autom Integrate Outlook into the workflow. Can send, draft, read, search, reply, forward, move, copy, and delete email; manage mail folders and attachments; and set categories and flags on messages. Can also list, create, update, delete, and respond to calendar events. Can be used in trigger mode to trigger a workflow when a new email is received. +## Scopes + +Connecting Outlook through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `Mail.ReadWrite` | Read and write Microsoft emails | +| `Mail.ReadBasic` | Read Microsoft emails | +| `Mail.Read` | Read Microsoft emails | +| `Mail.Send` | Send emails | +| `Calendars.ReadWrite` | Read and manage Outlook calendar events | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/pipedrive.mdx b/apps/docs/content/docs/integrations/pipedrive.mdx index 1cda528615d..4ff0c4ccd5c 100644 --- a/apps/docs/content/docs/integrations/pipedrive.mdx +++ b/apps/docs/content/docs/integrations/pipedrive.mdx @@ -32,6 +32,19 @@ In Sim, the Pipedrive integration enables your agents to interact with your sale Integrate Pipedrive into your workflow. Manage deals, contacts, sales pipeline, projects, activities, files, and communications with powerful CRM capabilities. +## Scopes + +Connecting Pipedrive through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `base` | Basic access to Pipedrive account | +| `deals:full` | Full access to manage Pipedrive deals | +| `contacts:full` | Full access to manage Pipedrive contacts | +| `leads:full` | Full access to manage Pipedrive leads | +| `activities:full` | Full access to manage Pipedrive activities | +| `mail:full` | Full access to manage Pipedrive emails | +| `projects:full` | Full access to manage Pipedrive projects | ## Actions diff --git a/apps/docs/content/docs/integrations/reddit.mdx b/apps/docs/content/docs/integrations/reddit.mdx index b1dee3befe6..4e5ae32f37b 100644 --- a/apps/docs/content/docs/integrations/reddit.mdx +++ b/apps/docs/content/docs/integrations/reddit.mdx @@ -27,6 +27,28 @@ These operations let your agents access and analyze Reddit content as part of yo Integrate Reddit into workflows. Read posts, comments, and search content. Submit posts, vote, reply, edit, manage messages, and access user and subreddit info. +## Scopes + +Connecting Reddit through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `identity` | Access Reddit identity | +| `read` | View posts, comments, and subreddits through your account | +| `submit` | Submit posts and comments | +| `vote` | Vote on posts and comments | +| `save` | Save and unsave posts and comments | +| `edit` | Edit posts and comments | +| `subscribe` | Subscribe and unsubscribe from subreddits | +| `history` | Access Reddit history | +| `privatemessages` | Access inbox and send private messages | +| `account` | Update account preferences and settings | +| `mysubreddits` | Access subscribed and moderated subreddits | +| `flair` | Manage user and post flair | +| `report` | Report posts and comments for rule violations | +| `modposts` | Approve, remove, and moderate posts in moderated subreddits | +| `modflair` | Manage flair in moderated subreddits | +| `modmail` | Access and respond to moderator mail | ## Actions diff --git a/apps/docs/content/docs/integrations/salesforce.mdx b/apps/docs/content/docs/integrations/salesforce.mdx index 3a51de3f3af..1fb8a43b9ca 100644 --- a/apps/docs/content/docs/integrations/salesforce.mdx +++ b/apps/docs/content/docs/integrations/salesforce.mdx @@ -30,6 +30,15 @@ The Salesforce tool is ideal for workflows where your agents need to streamline Integrate Salesforce into your workflow. Manage accounts, contacts, leads, opportunities, cases, and tasks, run reports and SOQL queries, and manage org schema by creating custom fields and objects via the Tooling API. +## Scopes + +Connecting Salesforce through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `api` | Access Salesforce API | +| `refresh_token` | Maintain long-term access to Salesforce account | +| `openid` | Standard authentication | ## Actions diff --git a/apps/docs/content/docs/integrations/sharepoint.mdx b/apps/docs/content/docs/integrations/sharepoint.mdx index a2014e1e1d2..0fc3b17b236 100644 --- a/apps/docs/content/docs/integrations/sharepoint.mdx +++ b/apps/docs/content/docs/integrations/sharepoint.mdx @@ -30,6 +30,19 @@ In Sim, the SharePoint integration empowers your agents to create and access Sha Integrate SharePoint into the workflow. Read/create pages, list sites, and work with lists (read, create, update items). Requires OAuth. +## Scopes + +Connecting SharePoint through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `openid` | Standard authentication | +| `profile` | Access profile information | +| `email` | Access email address | +| `Sites.Read.All` | Read SharePoint sites | +| `Sites.ReadWrite.All` | Read and write SharePoint sites | +| `Sites.Manage.All` | Manage SharePoint sites | +| `offline_access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/shopify.mdx b/apps/docs/content/docs/integrations/shopify.mdx index 5d91da714d7..66e0b617d0e 100644 --- a/apps/docs/content/docs/integrations/shopify.mdx +++ b/apps/docs/content/docs/integrations/shopify.mdx @@ -29,6 +29,18 @@ Use Sim's Shopify integration to automate common store management workflows—su Integrate Shopify into your workflow. Manage products, orders, customers, and inventory. Create, read, update, and delete products. List and manage orders. Handle customer data and adjust inventory levels. +## Scopes + +Connecting Shopify through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `write_products` | Read and manage Shopify products | +| `write_orders` | Read and manage Shopify orders | +| `write_customers` | Read and manage Shopify customers | +| `write_inventory` | Read and manage Shopify inventory levels | +| `read_locations` | View store locations | +| `write_merchant_managed_fulfillment_orders` | Read orders and create fulfillments for them | ## Actions diff --git a/apps/docs/content/docs/integrations/slack.mdx b/apps/docs/content/docs/integrations/slack.mdx index 47656dc9aa9..da355af4339 100644 --- a/apps/docs/content/docs/integrations/slack.mdx +++ b/apps/docs/content/docs/integrations/slack.mdx @@ -56,6 +56,31 @@ If you encounter issues with the Slack integration, contact us at [help@sim.ai]( Integrate Slack messaging and administration into a workflow. Custom Slack bots can manage Agent Sessions, stream incremental Markdown or structured chunks, react to Agent Session events, and configure Agent View suggested prompts. Standard messaging and management operations support both the Sim app and custom bot credentials. +## Scopes + +Connecting Slack through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `channels:read` | View public channels | +| `channels:history` | Read channel messages | +| `channels:manage` | Create, archive, and rename public channels | +| `groups:read` | View private channels | +| `groups:history` | Read private messages | +| `groups:write` | Create, archive, and manage private channels | +| `chat:write` | Send messages | +| `chat:write.public` | Post to public channels | +| `im:write` | Send direct messages | +| `im:read` | View direct message channels | +| `users:read` | View workspace users | +| `files:write` | Upload files | +| `files:read` | Download and read files | +| `canvases:read` | Read canvas sections | +| `canvases:write` | Create, edit, and delete canvas documents | +| `reactions:write` | Add emoji reactions to messages | +| `reactions:read` | View emoji reactions on messages | + +With `SLACK_EXTENDED_SCOPES` set, Sim also requests `assistant:write`, `app_mentions:read`, `im:history`. Add them to the app as well, or leave the flag unset. ## Actions diff --git a/apps/docs/content/docs/integrations/tiktok.mdx b/apps/docs/content/docs/integrations/tiktok.mdx index c3508797dc5..d715e0fabed 100644 --- a/apps/docs/content/docs/integrations/tiktok.mdx +++ b/apps/docs/content/docs/integrations/tiktok.mdx @@ -30,6 +30,17 @@ In Sim, the TikTok integration enables your agents to prepare content and report Integrate TikTok into your workflow. Get user profile information including follower counts and video statistics. List and query videos with cover images, embed links, and metadata. Send uploaded videos to the TikTok inbox as drafts for human review and publishing, then track post status. +## Scopes + +Connecting TikTok through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `user.info.basic` | Read a user's profile info \(open id, avatar, display name\) | +| `user.info.profile` | Read a user's profile info \(bio, verification status, username\) | +| `user.info.stats` | Read a user's stats \(follower, following, likes, and video counts\) | +| `video.upload` | Share content to a creator's account as a draft for further edit and post | +| `video.list` | Read a user's public TikTok videos | ## Actions diff --git a/apps/docs/content/docs/integrations/trello.mdx b/apps/docs/content/docs/integrations/trello.mdx index 9a4a8c817e6..d006927dbaa 100644 --- a/apps/docs/content/docs/integrations/trello.mdx +++ b/apps/docs/content/docs/integrations/trello.mdx @@ -40,6 +40,14 @@ Trello's authorization flow redirects back to Sim using a `return_url`. If your Integrate with Trello to list, search, create, update, and delete cards and lists, manage checklists and checklist items, assign labels and members, review activity, and add comments. +## Scopes + +Connecting Trello through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `read` | View boards, lists, and cards you can access | +| `write` | Create and modify boards, lists, and cards | ## Actions diff --git a/apps/docs/content/docs/integrations/wealthbox.mdx b/apps/docs/content/docs/integrations/wealthbox.mdx index c26ab70bd16..1f3486fe1ff 100644 --- a/apps/docs/content/docs/integrations/wealthbox.mdx +++ b/apps/docs/content/docs/integrations/wealthbox.mdx @@ -31,6 +31,14 @@ In Sim, the Wealthbox integration enables your agents to seamlessly interact wit Integrate Wealthbox into the workflow. Can read and write notes, read and write contacts, and read and write tasks. +## Scopes + +Connecting Wealthbox through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `login` | Access Wealthbox account | +| `data` | Access Wealthbox data | ## Actions diff --git a/apps/docs/content/docs/integrations/webflow.mdx b/apps/docs/content/docs/integrations/webflow.mdx index db906102450..5b96d509c6b 100644 --- a/apps/docs/content/docs/integrations/webflow.mdx +++ b/apps/docs/content/docs/integrations/webflow.mdx @@ -31,6 +31,17 @@ In Sim, the Webflow integration enables your agents to seamlessly interact with Integrates Webflow CMS into the workflow. Can create, get, list, update, or delete items in Webflow CMS collections. Manage your Webflow content programmatically. Can be used in trigger mode to trigger workflows when collection items change or forms are submitted. +## Scopes + +Connecting Webflow through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `cms:read` | View CMS content | +| `cms:write` | Manage CMS content | +| `sites:read` | View Webflow sites | +| `sites:write` | Manage webhooks and site settings | +| `forms:read` | View form submissions | ## Actions diff --git a/apps/docs/content/docs/integrations/wordpress.mdx b/apps/docs/content/docs/integrations/wordpress.mdx index 752b94e029b..ba37e839066 100644 --- a/apps/docs/content/docs/integrations/wordpress.mdx +++ b/apps/docs/content/docs/integrations/wordpress.mdx @@ -32,6 +32,13 @@ In Sim, the WordPress integration enables your agents to automate content publis Integrate with WordPress.com to create, update, and manage posts, pages, media, comments, categories, tags, and users. Connects to WordPress.com sites via OAuth. +## Scopes + +Connecting WordPress through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `global` | Full access to manage WordPress.com sites, posts, pages, media, and settings | ## Actions diff --git a/apps/docs/content/docs/integrations/x.mdx b/apps/docs/content/docs/integrations/x.mdx index 7018541c44d..68a21ccf604 100644 --- a/apps/docs/content/docs/integrations/x.mdx +++ b/apps/docs/content/docs/integrations/x.mdx @@ -32,6 +32,27 @@ In Sim, the X integration enables sophisticated social media automation scenario Integrate X into the workflow. Search tweets, manage bookmarks, follow/block/mute users, like and retweet, view trends, and more. +## Scopes + +Connecting X through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `tweet.read` | Read tweets and timeline | +| `tweet.write` | Post and delete tweets | +| `tweet.moderate.write` | Hide and unhide replies to tweets | +| `users.read` | Read user profiles and account information | +| `follows.read` | View followers and following lists | +| `follows.write` | Follow and unfollow users | +| `bookmark.read` | View bookmarked tweets | +| `bookmark.write` | Add and remove bookmarks | +| `like.read` | View liked tweets and liking users | +| `like.write` | Like and unlike tweets | +| `block.read` | View blocked users | +| `block.write` | Block and unblock users | +| `mute.read` | View muted users | +| `mute.write` | Mute and unmute users | +| `offline.access` | Access account when not using the application | ## Actions diff --git a/apps/docs/content/docs/integrations/zoho_desk.mdx b/apps/docs/content/docs/integrations/zoho_desk.mdx index 6786cba582c..7344ccf3dd7 100644 --- a/apps/docs/content/docs/integrations/zoho_desk.mdx +++ b/apps/docs/content/docs/integrations/zoho_desk.mdx @@ -40,6 +40,22 @@ To trigger on Zoho Desk activity instead, use the block's trigger mode. Sim crea Read and update Zoho Desk tickets, manage comments and threads, look up contacts, and download attachments. Can also trigger workflows from Zoho Desk webhook events. +## Scopes + +Connecting Zoho Desk through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `Desk.tickets.READ` | View tickets, threads, comments, and attachments | +| `Desk.tickets.UPDATE` | Update tickets and add comments | +| `Desk.contacts.READ` | View contacts | +| `Desk.articles.READ` | View Help Center articles | +| `Desk.organization.READ` | View organization details | +| `Desk.agents.READ` | View agents | +| `Desk.basic.READ` | View basic account and organization data | +| `Desk.webhooks.CREATE` | Create webhooks | +| `Desk.webhooks.DELETE` | Delete webhooks | +| `aaaserver.profile.READ` | View your Zoho profile | ## Actions diff --git a/apps/docs/content/docs/integrations/zoom.mdx b/apps/docs/content/docs/integrations/zoom.mdx index 3a124d73a73..7a087c4faf4 100644 --- a/apps/docs/content/docs/integrations/zoom.mdx +++ b/apps/docs/content/docs/integrations/zoom.mdx @@ -38,6 +38,23 @@ These capabilities let you streamline remote collaboration, automate recurring v Integrate Zoom into workflows. Create, list, update, and delete Zoom meetings. Get meeting details, invitations, recordings, and participants. Manage cloud recordings programmatically. +## Scopes + +Connecting Zoom through OAuth requests these scopes. + +| Scope | Description | +| ----- | ----------- | +| `user:read:user` | View Zoom profile information | +| `meeting:write:meeting` | Create Zoom meetings | +| `meeting:read:meeting` | View Zoom meeting details | +| `meeting:read:list_meetings` | List Zoom meetings | +| `meeting:update:meeting` | Update Zoom meetings | +| `meeting:delete:meeting` | Delete Zoom meetings | +| `meeting:read:invitation` | View Zoom meeting invitations | +| `meeting:read:list_past_participants` | View past meeting participants | +| `cloud_recording:read:list_user_recordings` | List Zoom cloud recordings | +| `cloud_recording:read:list_recording_files` | View recording files | +| `cloud_recording:delete:recording_file` | Delete cloud recordings | ## Actions diff --git a/apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx b/apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx index fe9bc70bb4a..23056734665 100644 --- a/apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx +++ b/apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx @@ -57,7 +57,9 @@ https://sim.yourdomain.com/api/auth/oauth2/callback/google-email https://sim.yourdomain.com/api/auth/oauth2/callback/google-drive ``` -Scopes are requested by Sim at authorization time; you generally do not need to pre-declare them, but Google and Microsoft require you to enable the corresponding APIs on the project/app first (for example Gmail API, Drive API, Calendar API). +Most providers let Sim request scopes at authorization time, but several need them declared on the app before anyone can connect. Microsoft Entra requires every delegated permission to be added to the registration, and some of them cannot be granted by the connecting user at all: a tenant admin has to consent on the directory's behalf. The directory and group permissions are where this bites: `microsoft-ad` asks for several, and Teams and Planner both request `Group.ReadWrite.All`. Check each permission against [Microsoft's reference](https://learn.microsoft.com/en-us/graph/permissions-reference) rather than assuming a user can approve it, since not every `.All` permission needs an admin (`Sites.Read.All`, for one, does not). Bitbucket fixes permissions on the consumer rather than per request. Google needs the matching API enabled on the Cloud project (Gmail API, Drive API, Calendar API, and so on) as well as the scopes on the consent screen. + +The [provider reference](#provider-reference) below lists the exact scopes each connector asks for, grouped by the app that covers it. @@ -94,58 +96,312 @@ Open a workflow, add the integration's block, and connect an account. A successf Every provider ID below maps to the redirect URI `https:///api/auth/oauth2/callback/`. +{/* GENERATED-START:oauth-apps */} + +Each table is what the OAuth flow requests, so it is what the app registration has to allow. A credential someone issues themselves instead (a service account, a pasted API token) carries its own access, which these do not govern. + +### Airtable + +`AIRTABLE_CLIENT_ID` / `AIRTABLE_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Airtable | `airtable` | `data.records:read`, `data.records:write`, `schema.bases:read`, `user.email:read`, `webhook:manage` | + +### Asana + +`ASANA_CLIENT_ID` / `ASANA_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Asana | `asana` | `default` | + +### Attio + +`ATTIO_CLIENT_ID` / `ATTIO_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Attio | `attio` | `record_permission:read-write`, `object_configuration:read-write`, `list_configuration:read-write`, `list_entry:read-write`, `note:read-write`, `task:read-write`, `comment:read-write`, `user_management:read`, `webhook:read-write` | + +### Bitbucket + +`BITBUCKET_CLIENT_ID` / `BITBUCKET_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Bitbucket | `bitbucket` | `account`, `repository`, `repository:write`, `pullrequest`, `pullrequest:write`, `pipeline`, `pipeline:write`, `webhook` | + +### Box + +`BOX_CLIENT_ID` / `BOX_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Box | `box` | `root_readwrite`, `sign_requests.readwrite` | + +### Cal.com + +`CALCOM_CLIENT_ID` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Cal.com | `calcom` | None to select | + +### ClickUp + +`CLICKUP_CLIENT_ID` / `CLICKUP_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| ClickUp | `clickup` | None to select | + +### Confluence + +`CONFLUENCE_CLIENT_ID` / `CONFLUENCE_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Confluence | `confluence` | `read:confluence-content.all`, `read:confluence-space.summary`, `read:space:confluence`, `write:confluence-content`, `write:confluence-space`, `write:confluence-file`, `read:page:confluence`, `write:page:confluence`, `read:comment:confluence`, `write:comment:confluence`, `delete:comment:confluence`, `delete:attachment:confluence`, `delete:page:confluence`, `read:label:confluence`, `write:label:confluence`, `read:attachment:confluence`, `write:attachment:confluence`, `search:confluence`, `read:me`, `offline_access`, `read:hierarchical-content:confluence`, `read:content.metadata:confluence`, `read:user:confluence`, `read:confluence-user`, `read:task:confluence`, `write:task:confluence`, `write:space:confluence`, `delete:space:confluence`, `read:blogpost:confluence`, `write:blogpost:confluence`, `delete:blogpost:confluence`, `read:content.property:confluence`, `write:content.property:confluence`, `read:space.property:confluence`, `write:space.property:confluence`, `read:space.permission:confluence` | + +### DocuSign + +`DOCUSIGN_CLIENT_ID` / `DOCUSIGN_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| DocuSign | `docusign` | `signature`, `extended` | + +### Dropbox + +`DROPBOX_CLIENT_ID` / `DROPBOX_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Dropbox | `dropbox` | `account_info.read`, `files.metadata.read`, `files.metadata.write`, `files.content.read`, `files.content.write`, `sharing.read`, `sharing.write` | + ### Google -One OAuth client in [Google Cloud Console](https://console.cloud.google.com/apis/credentials) covers all of these. Enable the matching API for each connector you use. +`GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` -| Environment variables | Provider IDs | -|---|---| -| `GOOGLE_CLIENT_ID`
`GOOGLE_CLIENT_SECRET` | `google-email`, `google-drive`, `google-sheets`, `google-docs`, `google-calendar`, `google-contacts`, `google-forms`, `google-tasks`, `google-meet`, `google-groups`, `google-chat`, `google-ads`, `google-bigquery`, `google-vault`, `vertex-ai` | +Every connector below also requests `https://www.googleapis.com/auth/userinfo.email`, `https://www.googleapis.com/auth/userinfo.profile`. + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Gmail | `google-email` | `https://www.googleapis.com/auth/gmail.send`, `https://www.googleapis.com/auth/gmail.modify`, `https://www.googleapis.com/auth/gmail.labels` | +| Google Drive | `google-drive` | `https://www.googleapis.com/auth/drive.file`, `https://www.googleapis.com/auth/drive` | +| Google Docs | `google-docs` | `https://www.googleapis.com/auth/drive.file`, `https://www.googleapis.com/auth/drive` | +| Google Sheets | `google-sheets` | `https://www.googleapis.com/auth/drive.file`, `https://www.googleapis.com/auth/drive` | +| Google Forms | `google-forms` | `https://www.googleapis.com/auth/drive`, `https://www.googleapis.com/auth/forms.body`, `https://www.googleapis.com/auth/forms.responses.readonly` | +| Google Calendar | `google-calendar` | `https://www.googleapis.com/auth/calendar` | +| Google Contacts | `google-contacts` | `https://www.googleapis.com/auth/contacts` | +| Google Ads | `google-ads` | `https://www.googleapis.com/auth/adwords` | +| Google BigQuery | `google-bigquery` | `https://www.googleapis.com/auth/bigquery` | +| Google Tasks | `google-tasks` | `https://www.googleapis.com/auth/tasks` | +| Google Vault | `google-vault` | `https://www.googleapis.com/auth/ediscovery`, `https://www.googleapis.com/auth/ediscovery.readonly`, `https://www.googleapis.com/auth/devstorage.read_only` | +| Google Groups | `google-groups` | `https://www.googleapis.com/auth/admin.directory.group`, `https://www.googleapis.com/auth/admin.directory.group.member` | +| Google Chat | `google-chat` | `https://www.googleapis.com/auth/chat.spaces.readonly`, `https://www.googleapis.com/auth/chat.messages.readonly` | +| Google Meet | `google-meet` | `https://www.googleapis.com/auth/meetings.space.created`, `https://www.googleapis.com/auth/meetings.space.readonly` | +| Vertex AI | `vertex-ai` | `https://www.googleapis.com/auth/cloud-platform` | + +A managed connection also requests `openid`, which is how Sim verifies the account behind the credential. + +### HubSpot + +`HUBSPOT_CLIENT_ID` / `HUBSPOT_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| HubSpot | `hubspot` | `crm.objects.contacts.read`, `crm.objects.contacts.write`, `crm.objects.companies.read`, `crm.objects.companies.write`, `crm.objects.deals.read`, `crm.objects.deals.write`, `crm.objects.owners.read`, `crm.objects.users.read`, `crm.objects.marketing_events.read`, `crm.objects.line_items.read`, `crm.objects.line_items.write`, `crm.objects.quotes.read`, `crm.objects.appointments.read`, `crm.objects.appointments.write`, `crm.objects.carts.read`, `sales-email-read`, `crm.lists.read`, `crm.lists.write`, `tickets`, `oauth` | -The same variables also power "Sign in with Google". See [Authentication](/platform/self-hosting/authentication). +### Instagram + +`INSTAGRAM_CLIENT_ID` / `INSTAGRAM_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Instagram | `instagram` | `instagram_business_basic`, `instagram_business_content_publish`, `instagram_business_manage_comments`, `instagram_business_manage_messages`, `instagram_business_manage_insights` | + +### Jira + +`JIRA_CLIENT_ID` / `JIRA_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Jira | `jira` | `read:jira-user`, `read:jira-work`, `write:jira-work`, `read:me`, `offline_access`, `read:issue.vote:jira`, `read:user:jira`, `delete:issue:jira`, `delete:comment:jira`, `delete:attachment:jira`, `delete:issue-worklog:jira`, `delete:issue-link:jira`, `read:servicedesk-request`, `write:servicedesk-request`, `manage:servicedesk-customer`, `read:servicedesk:jira-service-management`, `read:requesttype:jira-service-management`, `read:request:jira-service-management`, `write:request:jira-service-management`, `read:request.comment:jira-service-management`, `write:request.comment:jira-service-management`, `read:servicedesk.customer:jira-service-management`, `write:servicedesk.customer:jira-service-management`, `read:organization:jira-service-management`, `write:organization:jira-service-management`, `read:servicedesk.organization:jira-service-management`, `write:servicedesk.organization:jira-service-management`, `read:queue:jira-service-management`, `read:request.sla:jira-service-management`, `read:request.status:jira-service-management`, `write:request.status:jira-service-management`, `read:request.participant:jira-service-management`, `write:request.participant:jira-service-management`, `read:request.approval:jira-service-management`, `write:request.approval:jira-service-management`, `read:cmdb-object:jira`, `write:cmdb-object:jira`, `delete:cmdb-object:jira`, `read:cmdb-schema:jira`, `read:cmdb-type:jira`, `read:cmdb-attribute:jira` | + +### Linear + +`LINEAR_CLIENT_ID` / `LINEAR_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Linear | `linear` | `read`, `write` | + +### LinkedIn + +`LINKEDIN_CLIENT_ID` / `LINKEDIN_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| LinkedIn | `linkedin` | `profile`, `openid`, `email`, `w_member_social` | ### Microsoft -One app registration in [Entra ID](https://entra.microsoft.com) covers all of these. +`MICROSOFT_CLIENT_ID` / `MICROSOFT_CLIENT_SECRET` -| Environment variables | Provider IDs | -|---|---| -| `MICROSOFT_CLIENT_ID`
`MICROSOFT_CLIENT_SECRET` | `outlook`, `onedrive`, `sharepoint`, `microsoft-teams`, `microsoft-excel`, `microsoft-word`, `microsoft-planner`, `microsoft-dataverse`, `microsoft-ad` | +Every connector below also requests `openid`, `profile`, `email`, `offline_access`. -The same variables also power "Sign in with Microsoft". +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Azure AD | `microsoft-ad` | `User.ReadWrite.All`, `Group.ReadWrite.All`, `GroupMember.ReadWrite.All`, `LicenseAssignment.Read.All`, `LicenseAssignment.ReadWrite.All`, `UserAuthenticationMethod.ReadWrite.All`, `AuditLog.Read.All`, `Application.Read.All`, `AppRoleAssignment.ReadWrite.All`, `RoleManagement.ReadWrite.Directory`, `Device.Read.All`, `Policy.Read.All` | +| Microsoft Dataverse | `microsoft-dataverse` | `https://dynamics.microsoft.com/user_impersonation` | +| Microsoft Excel | `microsoft-excel` | `Files.Read`, `Files.ReadWrite`, `Files.Read.All`, `Files.ReadWrite.All`, `Sites.Read.All` | +| Microsoft Planner | `microsoft-planner` | `Group.ReadWrite.All`, `Group.Read.All`, `Tasks.ReadWrite` | +| Microsoft Teams | `microsoft-teams` | `User.Read`, `Chat.Read`, `Chat.ReadWrite`, `Chat.ReadBasic`, `ChatMessage.Send`, `Channel.ReadBasic.All`, `ChannelMessage.Send`, `ChannelMessage.Read.All`, `ChannelMessage.ReadWrite`, `ChannelMember.Read.All`, `Group.Read.All`, `Group.ReadWrite.All`, `Team.ReadBasic.All`, `TeamMember.Read.All`, `Files.Read`, `Sites.Read.All` | +| Microsoft Word | `microsoft-word` | `Files.Read`, `Files.ReadWrite`, `Files.Read.All`, `Files.ReadWrite.All` | +| Outlook | `outlook` | `Mail.ReadWrite`, `Mail.ReadBasic`, `Mail.Read`, `Mail.Send`, `Calendars.ReadWrite` | +| OneDrive | `onedrive` | `Files.Read`, `Files.ReadWrite` | +| SharePoint | `sharepoint` | `Sites.Read.All`, `Sites.ReadWrite.All`, `Sites.Manage.All` | -### Everything else +Connecting a specific environment requests that environment's `/.default` scope instead of the resource scope above. The host is normalized to its `.api` form first, so `https://contoso.crm.dynamics.com` is requested as `https://contoso.api.crm.dynamics.com/.default`. -| Service | Environment variables | Provider ID | -|---|---|---| -| Slack | `SLACK_CLIENT_ID` / `SLACK_CLIENT_SECRET` | `slack` | -| Notion | `NOTION_CLIENT_ID` / `NOTION_CLIENT_SECRET` | `notion` | -| Jira | `JIRA_CLIENT_ID` / `JIRA_CLIENT_SECRET` | `jira` | -| Confluence | `CONFLUENCE_CLIENT_ID` / `CONFLUENCE_CLIENT_SECRET` | `confluence` | -| Linear | `LINEAR_CLIENT_ID` / `LINEAR_CLIENT_SECRET` | `linear` | -| Asana | `ASANA_CLIENT_ID` / `ASANA_CLIENT_SECRET` | `asana` | -| ClickUp | `CLICKUP_CLIENT_ID` / `CLICKUP_CLIENT_SECRET` | `clickup` | -| Monday | `MONDAY_CLIENT_ID` / `MONDAY_CLIENT_SECRET` | `monday` | -| Airtable | `AIRTABLE_CLIENT_ID` / `AIRTABLE_CLIENT_SECRET` | `airtable` | -| Bitbucket | `BITBUCKET_CLIENT_ID` / `BITBUCKET_CLIENT_SECRET` | `bitbucket` | -| HubSpot | `HUBSPOT_CLIENT_ID` / `HUBSPOT_CLIENT_SECRET` | `hubspot` | -| Salesforce | `SALESFORCE_CLIENT_ID` / `SALESFORCE_CLIENT_SECRET` | `salesforce` | -| Pipedrive | `PIPEDRIVE_CLIENT_ID` / `PIPEDRIVE_CLIENT_SECRET` | `pipedrive` | -| Attio | `ATTIO_CLIENT_ID` / `ATTIO_CLIENT_SECRET` | `attio` | -| Zoho Desk | `ZOHO_CLIENT_ID` / `ZOHO_CLIENT_SECRET` | `zoho-desk` | -| Wealthbox | `WEALTHBOX_CLIENT_ID` / `WEALTHBOX_CLIENT_SECRET` | `wealthbox` | -| Box | `BOX_CLIENT_ID` / `BOX_CLIENT_SECRET` | `box` | -| Dropbox | `DROPBOX_CLIENT_ID` / `DROPBOX_CLIENT_SECRET` | `dropbox` | -| DocuSign | `DOCUSIGN_CLIENT_ID` / `DOCUSIGN_CLIENT_SECRET` | `docusign` | -| Zoom | `ZOOM_CLIENT_ID` / `ZOOM_CLIENT_SECRET` | `zoom` | -| Cal.com | `CALCOM_CLIENT_ID` only — PKCE public client, no secret | `calcom` | -| Webflow | `WEBFLOW_CLIENT_ID` / `WEBFLOW_CLIENT_SECRET` | `webflow` | -| WordPress | `WORDPRESS_CLIENT_ID` / `WORDPRESS_CLIENT_SECRET` | `wordpress` | -| LinkedIn | `LINKEDIN_CLIENT_ID` / `LINKEDIN_CLIENT_SECRET` | `linkedin` | -| X | `X_CLIENT_ID` / `X_CLIENT_SECRET` | `x` | -| Reddit | `REDDIT_CLIENT_ID` / `REDDIT_CLIENT_SECRET` | `reddit` | -| Spotify | `SPOTIFY_CLIENT_ID` / `SPOTIFY_CLIENT_SECRET` | `spotify` | -| TikTok | `TIKTOK_CLIENT_ID` / `TIKTOK_CLIENT_SECRET` | `tiktok` | +### Monday.com + +`MONDAY_CLIENT_ID` / `MONDAY_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Monday.com | `monday` | `boards:read`, `boards:write`, `updates:read`, `updates:write`, `webhooks:read`, `webhooks:write`, `me:read` | + +### Notion + +`NOTION_CLIENT_ID` / `NOTION_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Notion | `notion` | None to select | + +### Pipedrive + +`PIPEDRIVE_CLIENT_ID` / `PIPEDRIVE_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Pipedrive | `pipedrive` | `base`, `deals:full`, `contacts:full`, `leads:full`, `activities:full`, `mail:full`, `projects:full` | + +### Reddit + +`REDDIT_CLIENT_ID` / `REDDIT_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Reddit | `reddit` | `identity`, `read`, `submit`, `vote`, `save`, `edit`, `subscribe`, `history`, `privatemessages`, `account`, `mysubreddits`, `flair`, `report`, `modposts`, `modflair`, `modmail` | + +### Salesforce + +`SALESFORCE_CLIENT_ID` / `SALESFORCE_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Salesforce | `salesforce`
`salesforce-sandbox` | `api`, `refresh_token`, `openid` | + +### Shopify + +`SHOPIFY_CLIENT_ID` / `SHOPIFY_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Shopify | `shopify` | `write_products`, `write_orders`, `write_customers`, `write_inventory`, `read_locations`, `write_merchant_managed_fulfillment_orders` | + +### Slack + +`SLACK_CLIENT_ID` / `SLACK_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Slack | `slack` | `channels:read`, `channels:history`, `channels:manage`, `groups:read`, `groups:history`, `groups:write`, `chat:write`, `chat:write.public`, `im:write`, `im:read`, `users:read`, `files:write`, `files:read`, `canvases:read`, `canvases:write`, `reactions:write`, `reactions:read` | + +With `SLACK_EXTENDED_SCOPES` set, Sim also requests `assistant:write`, `app_mentions:read`, `im:history`. Add them to the app as well, or leave the flag unset. + +### Spotify + +`SPOTIFY_CLIENT_ID` / `SPOTIFY_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Spotify | `spotify` | `user-read-private`, `user-read-email`, `user-library-read`, `user-library-modify`, `playlist-read-private`, `playlist-read-collaborative`, `playlist-modify-public`, `playlist-modify-private`, `user-read-playback-state`, `user-modify-playback-state`, `user-read-currently-playing`, `user-read-recently-played`, `user-top-read`, `user-follow-read`, `user-follow-modify`, `user-read-playback-position`, `ugc-image-upload` | + +### TikTok + +`TIKTOK_CLIENT_ID` / `TIKTOK_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| TikTok | `tiktok` | `user.info.basic`, `user.info.profile`, `user.info.stats`, `video.upload`, `video.list` | + +### Trello + +`TRELLO_API_KEY` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Trello | `trello` | `read`, `write` | + +Trello is API-key based rather than OAuth 2.0 and calls back to `/api/auth/trello/callback`. + +### Wealthbox + +`WEALTHBOX_CLIENT_ID` / `WEALTHBOX_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Wealthbox | `wealthbox` | `login`, `data` | + +### Webflow + +`WEBFLOW_CLIENT_ID` / `WEBFLOW_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Webflow | `webflow` | `cms:read`, `cms:write`, `sites:read`, `sites:write`, `forms:read` | + +### WordPress + +`WORDPRESS_CLIENT_ID` / `WORDPRESS_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| WordPress | `wordpress` | `global` | + +### X + +`X_CLIENT_ID` / `X_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| X | `x` | `tweet.read`, `tweet.write`, `tweet.moderate.write`, `users.read`, `follows.read`, `follows.write`, `bookmark.read`, `bookmark.write`, `like.read`, `like.write`, `block.read`, `block.write`, `mute.read`, `mute.write`, `offline.access` | + +### Zoho Desk + +`ZOHO_CLIENT_ID` / `ZOHO_CLIENT_SECRET` + +Every connector below also requests `aaaserver.profile.READ`. + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Zoho Desk | `zoho-desk` | `Desk.tickets.READ`, `Desk.tickets.UPDATE`, `Desk.contacts.READ`, `Desk.articles.READ`, `Desk.organization.READ`, `Desk.agents.READ`, `Desk.basic.READ`, `Desk.webhooks.CREATE`, `Desk.webhooks.DELETE` | +| ManageEngine ServiceDesk Plus | `manageengine-sdp` | `SDPOnDemand.requests.CREATE`, `SDPOnDemand.requests.READ`, `SDPOnDemand.requests.UPDATE`, `SDPOnDemand.requests.DELETE`, `SDPOnDemand.problems.CREATE`, `SDPOnDemand.problems.READ`, `SDPOnDemand.problems.UPDATE`, `SDPOnDemand.problems.DELETE`, `SDPOnDemand.changes.CREATE`, `SDPOnDemand.changes.READ`, `SDPOnDemand.changes.UPDATE`, `SDPOnDemand.changes.DELETE`, `SDPOnDemand.assets.CREATE`, `SDPOnDemand.assets.READ`, `SDPOnDemand.assets.UPDATE`, `SDPOnDemand.assets.DELETE`, `SDPOnDemand.solutions.CREATE`, `SDPOnDemand.solutions.READ`, `SDPOnDemand.solutions.UPDATE`, `SDPOnDemand.solutions.DELETE` | + +### Zoom + +`ZOOM_CLIENT_ID` / `ZOOM_CLIENT_SECRET` + +| Connector | Provider ID | Scopes to grant | +| --- | --- | --- | +| Zoom | `zoom` | `user:read:user`, `meeting:write:meeting`, `meeting:read:meeting`, `meeting:read:list_meetings`, `meeting:update:meeting`, `meeting:delete:meeting`, `meeting:read:invitation`, `meeting:read:list_past_participants`, `cloud_recording:read:list_user_recordings`, `cloud_recording:read:list_recording_files`, `cloud_recording:delete:recording_file` | + +{/* GENERATED-END:oauth-apps */} For Bitbucket, create an OAuth consumer and register `https:///api/auth/oauth2/callback/bitbucket` as its callback URL. Bitbucket fixes @@ -199,7 +455,7 @@ Webhook triggers receive callbacks from the provider and must be able to verify Your deployment must also be reachable from the provider's servers for webhook triggers to fire — a Sim instance on a private network can use polling triggers but not webhook triggers. Polling triggers additionally require the scheduler; see [Background Jobs](/platform/self-hosting/background-jobs). ({ parseProvider: (provider: string) => ({ baseProvider: provider }), })) -vi.mock('@/lib/oauth/utils', () => ({ +vi.mock('@/lib/oauth/scopes', () => ({ getScopeDescription: (scope: string) => scope, +})) + +vi.mock('@/lib/oauth/utils', () => ({ getServiceConfigByProviderId: () => null, })) diff --git a/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx b/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx index 9b76a0425c5..e7c3721e807 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx @@ -25,7 +25,8 @@ import { type OAuthProvider, parseProvider, } from '@/lib/oauth' -import { getScopeDescription, getServiceConfigByProviderId } from '@/lib/oauth/utils' +import { getScopeDescription } from '@/lib/oauth/scopes' +import { getServiceConfigByProviderId } from '@/lib/oauth/utils' import { MicrosoftDataverseEnvironmentField, useMicrosoftDataverseEnvironmentForm, diff --git a/apps/sim/blocks/blocks/microsoft_ad.test.ts b/apps/sim/blocks/blocks/microsoft_ad.test.ts index 8d0b8c52ff2..3ae8579aad9 100644 --- a/apps/sim/blocks/blocks/microsoft_ad.test.ts +++ b/apps/sim/blocks/blocks/microsoft_ad.test.ts @@ -2,7 +2,7 @@ * @vitest-environment node */ import { describe, expect, it } from 'vitest' -import { SCOPE_DESCRIPTIONS } from '@/lib/oauth/utils' +import { SCOPE_DESCRIPTIONS } from '@/lib/oauth/scopes' import { MicrosoftAdBlock } from '@/blocks/blocks/microsoft_ad' import * as microsoftAdTools from '@/tools/microsoft_ad' diff --git a/apps/sim/connectors/microsoft-excel/meta.ts b/apps/sim/connectors/microsoft-excel/meta.ts index 463993e4070..a0052b33fec 100644 --- a/apps/sim/connectors/microsoft-excel/meta.ts +++ b/apps/sim/connectors/microsoft-excel/meta.ts @@ -11,7 +11,13 @@ export const microsoftExcelConnectorMeta: ConnectorMeta = { auth: { mode: 'oauth', provider: 'microsoft-excel', - requiredScopes: ['Files.ReadWrite'], + /** + * `Files.ReadWrite` alone reaches only the user's own OneDrive, so a + * connector pointed at a `driveId` needs the `.All` grant as well. Both are + * listed so a credential predating that scope is reported as needing + * reauthorization instead of failing at sync time. + */ + requiredScopes: ['Files.ReadWrite', 'Files.ReadWrite.All'], }, /** diff --git a/apps/sim/lib/auth/connectors/managed-oauth.ts b/apps/sim/lib/auth/connectors/managed-oauth.ts index 8d63e37de79..de6aacadecc 100644 --- a/apps/sim/lib/auth/connectors/managed-oauth.ts +++ b/apps/sim/lib/auth/connectors/managed-oauth.ts @@ -9,6 +9,7 @@ import { readResponseJsonWithLimit } from '@/lib/core/utils/stream-limits' import { getDocusignOAuthUrl } from '@/lib/oauth/docusign' import { deriveMicrosoftEmailVerified, mapMicrosoftProfileToUser } from '@/lib/oauth/microsoft' import { SALESFORCE_LOGIN_HOSTS } from '@/lib/oauth/salesforce' +import { GOOGLE_MANAGED_OAUTH_PROVIDER_IDS } from '@/lib/oauth/scopes' import { isTerminalRefreshError } from '@/lib/oauth/terminal-errors' import { getCanonicalScopesForProvider } from '@/lib/oauth/utils' import { MONDAY_API_URL, MONDAY_API_VERSION } from '@/tools/monday/utils' @@ -1287,16 +1288,7 @@ export function getManagedOAuthConnectorPolicy( function resolveManagedOAuthPolicy( providerId: string ): (() => ManagedOAuthConnectorConfig) | undefined { - if ( - providerId === 'google-email' || - providerId === 'google-calendar' || - providerId === 'google-drive' || - providerId === 'google-docs' || - providerId === 'google-forms' || - providerId === 'google-chat' || - providerId === 'google-meet' || - providerId === 'google-sheets' - ) { + if (GOOGLE_MANAGED_OAUTH_PROVIDER_IDS.has(providerId)) { return () => createGoogleManagedOAuthConnector(providerId) } if (providerId === 'confluence' || providerId === 'jira') { diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index 692a59510fe..b8d48c3ed0a 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -85,6 +85,7 @@ import { SALESFORCE_LOGIN_HOSTS, SALESFORCE_PROVIDER_ID_LABELS, } from '@/lib/oauth/salesforce' +import { ENV_GATED_SCOPES, OAUTH_SCOPES } from '@/lib/oauth/scopes' import { REDDIT_USER_AGENT } from '@/tools/reddit/constants' import type { OAuthProviderConfig } from './types' @@ -97,7 +98,7 @@ const logger = createLogger('OAuth') * app's approved list, so these stay out of the default grant. */ export function getSlackApprovalGatedScopes(enabled: boolean): readonly string[] { - return enabled ? ['assistant:write', 'app_mentions:read', 'im:history'] : [] + return enabled ? ENV_GATED_SCOPES.slack.scopes : [] } const SLACK_APPROVAL_GATED_SCOPES = getSlackApprovalGatedScopes(isSlackExtendedScopesEnabled) @@ -114,7 +115,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'claude-platform-service-account', icon: ClaudeIcon, baseProviderIcon: ClaudeIcon, - scopes: [], + scopes: [...OAUTH_SCOPES['claude-platform']], authType: 'service_account', }, }, @@ -130,13 +131,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-email', icon: GmailIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/gmail.send', - 'https://www.googleapis.com/auth/gmail.modify', - 'https://www.googleapis.com/auth/gmail.labels', - ], + scopes: [...OAUTH_SCOPES.gmail], serviceAccountProviderId: 'google-service-account', }, 'google-drive': { @@ -145,12 +140,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-drive', icon: GoogleDriveIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/drive.file', - 'https://www.googleapis.com/auth/drive', - ], + scopes: [...OAUTH_SCOPES['google-drive']], serviceAccountProviderId: 'google-service-account', }, 'google-docs': { @@ -159,12 +149,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-docs', icon: GoogleDocsIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/drive.file', - 'https://www.googleapis.com/auth/drive', - ], + scopes: [...OAUTH_SCOPES['google-docs']], serviceAccountProviderId: 'google-service-account', }, 'google-sheets': { @@ -173,12 +158,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-sheets', icon: GoogleSheetsIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/drive.file', - 'https://www.googleapis.com/auth/drive', - ], + scopes: [...OAUTH_SCOPES['google-sheets']], serviceAccountProviderId: 'google-service-account', }, 'google-forms': { @@ -187,13 +167,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-forms', icon: GoogleFormsIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/drive', - 'https://www.googleapis.com/auth/forms.body', - 'https://www.googleapis.com/auth/forms.responses.readonly', - ], + scopes: [...OAUTH_SCOPES['google-forms']], serviceAccountProviderId: 'google-service-account', }, 'google-calendar': { @@ -202,11 +176,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-calendar', icon: GoogleCalendarIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/calendar', - ], + scopes: [...OAUTH_SCOPES['google-calendar']], serviceAccountProviderId: 'google-service-account', }, 'google-contacts': { @@ -215,11 +185,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-contacts', icon: GoogleContactsIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/contacts', - ], + scopes: [...OAUTH_SCOPES['google-contacts']], serviceAccountProviderId: 'google-service-account', }, 'google-ads': { @@ -228,11 +194,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-ads', icon: GoogleAdsIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/adwords', - ], + scopes: [...OAUTH_SCOPES['google-ads']], }, 'google-bigquery': { name: 'Google BigQuery', @@ -240,11 +202,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-bigquery', icon: GoogleBigQueryIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/bigquery', - ], + scopes: [...OAUTH_SCOPES['google-bigquery']], serviceAccountProviderId: 'google-service-account', }, 'google-tasks': { @@ -253,11 +211,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-tasks', icon: GoogleTasksIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/tasks', - ], + scopes: [...OAUTH_SCOPES['google-tasks']], serviceAccountProviderId: 'google-service-account', }, 'google-vault': { @@ -266,17 +220,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-vault', icon: GoogleVaultIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/ediscovery', - // Least-privilege scope for read-only consumers. The knowledge base - // connector only lists matters, holds, and saved queries, all of which - // accept ediscovery.readonly; the block's export tools still need the - // read-write scope above. - 'https://www.googleapis.com/auth/ediscovery.readonly', - 'https://www.googleapis.com/auth/devstorage.read_only', - ], + scopes: [...OAUTH_SCOPES['google-vault']], serviceAccountProviderId: 'google-service-account', }, 'google-groups': { @@ -285,12 +229,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-groups', icon: GoogleGroupsIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/admin.directory.group', - 'https://www.googleapis.com/auth/admin.directory.group.member', - ], + scopes: [...OAUTH_SCOPES['google-groups']], serviceAccountProviderId: 'google-service-account', }, /** @@ -307,12 +246,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-chat', icon: GoogleChatIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/chat.spaces.readonly', - 'https://www.googleapis.com/auth/chat.messages.readonly', - ], + scopes: [...OAUTH_SCOPES['google-chat']], }, 'google-meet': { name: 'Google Meet', @@ -320,12 +254,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-meet', icon: GoogleMeetIcon, baseProviderIcon: GoogleIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/meetings.space.created', - 'https://www.googleapis.com/auth/meetings.space.readonly', - ], + scopes: [...OAUTH_SCOPES['google-meet']], serviceAccountProviderId: 'google-service-account', }, 'google-service-account': { @@ -334,7 +263,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'google-service-account', icon: GoogleIcon, baseProviderIcon: GoogleIcon, - scopes: [], + scopes: [...OAUTH_SCOPES['google-service-account']], authType: 'service_account', }, 'vertex-ai': { @@ -343,11 +272,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'vertex-ai', icon: VertexIcon, baseProviderIcon: VertexIcon, - scopes: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/cloud-platform', - ], + scopes: [...OAUTH_SCOPES['vertex-ai']], }, }, defaultService: 'gmail', @@ -362,24 +287,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'microsoft-ad', icon: AzureIcon, baseProviderIcon: MicrosoftIcon, - scopes: [ - 'openid', - 'profile', - 'email', - 'User.ReadWrite.All', - 'Group.ReadWrite.All', - 'GroupMember.ReadWrite.All', - 'LicenseAssignment.Read.All', - 'LicenseAssignment.ReadWrite.All', - 'UserAuthenticationMethod.ReadWrite.All', - 'AuditLog.Read.All', - 'Application.Read.All', - 'AppRoleAssignment.ReadWrite.All', - 'RoleManagement.ReadWrite.Directory', - 'Device.Read.All', - 'Policy.Read.All', - 'offline_access', - ], + scopes: [...OAUTH_SCOPES['microsoft-ad']], }, 'microsoft-dataverse': { name: 'Microsoft Dataverse', @@ -387,13 +295,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'microsoft-dataverse', icon: MicrosoftDataverseIcon, baseProviderIcon: MicrosoftIcon, - scopes: [ - 'openid', - 'profile', - 'email', - 'https://dynamics.microsoft.com/user_impersonation', - 'offline_access', - ], + scopes: [...OAUTH_SCOPES['microsoft-dataverse']], }, 'microsoft-excel': { name: 'Microsoft Excel', @@ -401,7 +303,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'microsoft-excel', icon: MicrosoftExcelIcon, baseProviderIcon: MicrosoftIcon, - scopes: ['openid', 'profile', 'email', 'Files.Read', 'Files.ReadWrite', 'offline_access'], + scopes: [...OAUTH_SCOPES['microsoft-excel']], }, 'microsoft-planner': { name: 'Microsoft Planner', @@ -409,15 +311,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'microsoft-planner', icon: MicrosoftPlannerIcon, baseProviderIcon: MicrosoftIcon, - scopes: [ - 'openid', - 'profile', - 'email', - 'Group.ReadWrite.All', - 'Group.Read.All', - 'Tasks.ReadWrite', - 'offline_access', - ], + scopes: [...OAUTH_SCOPES['microsoft-planner']], }, 'microsoft-teams': { name: 'Microsoft Teams', @@ -425,28 +319,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'microsoft-teams', icon: MicrosoftTeamsIcon, baseProviderIcon: MicrosoftIcon, - scopes: [ - 'openid', - 'profile', - 'email', - 'User.Read', - 'Chat.Read', - 'Chat.ReadWrite', - 'Chat.ReadBasic', - 'ChatMessage.Send', - 'Channel.ReadBasic.All', - 'ChannelMessage.Send', - 'ChannelMessage.Read.All', - 'ChannelMessage.ReadWrite', - 'ChannelMember.Read.All', - 'Group.Read.All', - 'Group.ReadWrite.All', - 'Team.ReadBasic.All', - 'TeamMember.Read.All', - 'offline_access', - 'Files.Read', - 'Sites.Read.All', - ], + scopes: [...OAUTH_SCOPES['microsoft-teams']], }, 'microsoft-word': { name: 'Microsoft Word', @@ -454,30 +327,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'microsoft-word', icon: MicrosoftWordIcon, baseProviderIcon: MicrosoftIcon, - /** - * Word documents are ordinary drive items, so the integration reads and - * writes them through the Files permissions rather than a Word-specific - * scope — Microsoft Graph exposes no Word API of its own. - * - * The `.All` variants are what make the SharePoint drive the block - * exposes actually work: `Files.ReadWrite` alone covers only the signed-in - * user's own OneDrive, so a document library would be rejected for - * insufficient privileges. Both are user-consentable, so this does not - * push the integration behind admin consent, and neither grants access to - * anything the signed-in account could not already open. - * - * @see https://learn.microsoft.com/en-us/graph/permissions-reference - */ - scopes: [ - 'openid', - 'profile', - 'email', - 'Files.Read', - 'Files.ReadWrite', - 'Files.Read.All', - 'Files.ReadWrite.All', - 'offline_access', - ], + scopes: [...OAUTH_SCOPES['microsoft-word']], }, outlook: { name: 'Outlook', @@ -485,37 +335,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'outlook', icon: OutlookIcon, baseProviderIcon: MicrosoftIcon, - /** - * `Calendars.ReadWrite` backs the Outlook calendar operations. Graph documents it - * as the sole accepted permission for creating and updating events and for - * accept / tentativelyAccept / decline ("Higher: Not available"), and it is - * supported for both work/school and personal Microsoft accounts. - * - * Do NOT add `Calendars.ReadWrite.Shared` here. This provider is shared by work - * and personal Outlook accounts, and the `.Shared` calendar scopes are not - * confirmed supported for personal Microsoft accounts — requesting one risks - * failing consent for personal users, which would take mail access down with it. - * That is the same reasoning that kept `findMeetingTimes` out of this integration. - * The consequence is that calendar operations target calendars the account owns; - * picking a calendar shared by another user may return 403 from Graph. - * - * Microsoft only grants newly-added scopes on a fresh authorization, so users who - * connected Outlook before `Calendars.ReadWrite` existed must reconnect - * (re-consent) before the calendar operations will work. - * - * @see https://learn.microsoft.com/en-us/graph/permissions-reference - */ - scopes: [ - 'openid', - 'profile', - 'email', - 'Mail.ReadWrite', - 'Mail.ReadBasic', - 'Mail.Read', - 'Mail.Send', - 'Calendars.ReadWrite', - 'offline_access', - ], + scopes: [...OAUTH_SCOPES.outlook], }, onedrive: { name: 'OneDrive', @@ -523,7 +343,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'onedrive', icon: MicrosoftOneDriveIcon, baseProviderIcon: MicrosoftIcon, - scopes: ['openid', 'profile', 'email', 'Files.Read', 'Files.ReadWrite', 'offline_access'], + scopes: [...OAUTH_SCOPES.onedrive], }, sharepoint: { name: 'SharePoint', @@ -531,15 +351,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'sharepoint', icon: MicrosoftSharepointIcon, baseProviderIcon: MicrosoftIcon, - scopes: [ - 'openid', - 'profile', - 'email', - 'Sites.Read.All', - 'Sites.ReadWrite.All', - 'Sites.Manage.All', - 'offline_access', - ], + scopes: [...OAUTH_SCOPES.sharepoint], }, }, defaultService: 'outlook', @@ -554,23 +366,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'x', icon: xIcon, baseProviderIcon: xIcon, - scopes: [ - 'tweet.read', - 'tweet.write', - 'tweet.moderate.write', - 'users.read', - 'follows.read', - 'follows.write', - 'bookmark.read', - 'bookmark.write', - 'like.read', - 'like.write', - 'block.read', - 'block.write', - 'mute.read', - 'mute.write', - 'offline.access', - ], + scopes: [...OAUTH_SCOPES.x], }, }, defaultService: 'x', @@ -585,13 +381,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'tiktok', icon: TikTokIcon, baseProviderIcon: TikTokIcon, - scopes: [ - 'user.info.basic', - 'user.info.profile', - 'user.info.stats', - 'video.upload', - 'video.list', - ], + scopes: [...OAUTH_SCOPES.tiktok], }, }, defaultService: 'tiktok', @@ -607,7 +397,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'atlassian-service-account', icon: AtlassianIcon, baseProviderIcon: AtlassianIcon, - scopes: [], + scopes: [...OAUTH_SCOPES['atlassian-service-account']], authType: 'service_account', }, }, @@ -624,44 +414,7 @@ export const OAUTH_PROVIDERS: Record = { icon: ConfluenceIcon, baseProviderIcon: ConfluenceIcon, serviceAccountProviderId: 'atlassian-service-account', - scopes: [ - 'read:confluence-content.all', - 'read:confluence-space.summary', - 'read:space:confluence', - 'write:confluence-content', - 'write:confluence-space', - 'write:confluence-file', - 'read:page:confluence', - 'write:page:confluence', - 'read:comment:confluence', - 'write:comment:confluence', - 'delete:comment:confluence', - 'delete:attachment:confluence', - 'delete:page:confluence', - 'read:label:confluence', - 'write:label:confluence', - 'read:attachment:confluence', - 'write:attachment:confluence', - 'search:confluence', - 'read:me', - 'offline_access', - 'read:hierarchical-content:confluence', - 'read:content.metadata:confluence', - 'read:user:confluence', - 'read:confluence-user', - 'read:task:confluence', - 'write:task:confluence', - 'write:space:confluence', - 'delete:space:confluence', - 'read:blogpost:confluence', - 'write:blogpost:confluence', - 'delete:blogpost:confluence', - 'read:content.property:confluence', - 'write:content.property:confluence', - 'read:space.property:confluence', - 'write:space.property:confluence', - 'read:space.permission:confluence', - ], + scopes: [...OAUTH_SCOPES.confluence], }, }, defaultService: 'confluence', @@ -677,52 +430,7 @@ export const OAUTH_PROVIDERS: Record = { icon: JiraIcon, baseProviderIcon: JiraIcon, serviceAccountProviderId: 'atlassian-service-account', - scopes: [ - 'read:jira-user', - 'read:jira-work', - 'write:jira-work', - 'read:me', - 'offline_access', - 'read:issue.vote:jira', - 'read:user:jira', - 'delete:issue:jira', - 'delete:comment:jira', - 'delete:attachment:jira', - 'delete:issue-worklog:jira', - 'delete:issue-link:jira', - // Jira Service Management scopes. The classic scopes are required: Atlassian - // enforces an endpoint's granular scope set as all-of, and several JSM request - // endpoints include scopes outside this list in their granular sets. - 'read:servicedesk-request', - 'write:servicedesk-request', - 'manage:servicedesk-customer', - 'read:servicedesk:jira-service-management', - 'read:requesttype:jira-service-management', - 'read:request:jira-service-management', - 'write:request:jira-service-management', - 'read:request.comment:jira-service-management', - 'write:request.comment:jira-service-management', - 'read:servicedesk.customer:jira-service-management', - 'write:servicedesk.customer:jira-service-management', - 'read:organization:jira-service-management', - 'write:organization:jira-service-management', - 'read:servicedesk.organization:jira-service-management', - 'write:servicedesk.organization:jira-service-management', - 'read:queue:jira-service-management', - 'read:request.sla:jira-service-management', - 'read:request.status:jira-service-management', - 'write:request.status:jira-service-management', - 'read:request.participant:jira-service-management', - 'write:request.participant:jira-service-management', - 'read:request.approval:jira-service-management', - 'write:request.approval:jira-service-management', - 'read:cmdb-object:jira', - 'write:cmdb-object:jira', - 'delete:cmdb-object:jira', - 'read:cmdb-schema:jira', - 'read:cmdb-type:jira', - 'read:cmdb-attribute:jira', - ], + scopes: [...OAUTH_SCOPES.jira], }, }, defaultService: 'jira', @@ -738,13 +446,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'airtable-service-account', icon: AirtableIcon, baseProviderIcon: AirtableIcon, - scopes: [ - 'data.records:read', - 'data.records:write', - 'schema.bases:read', - 'user.email:read', - 'webhook:manage', - ], + scopes: [...OAUTH_SCOPES.airtable], }, }, defaultService: 'airtable', @@ -759,16 +461,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'bitbucket', icon: BitbucketIcon, baseProviderIcon: BitbucketIcon, - scopes: [ - 'account', - 'repository', - 'repository:write', - 'pullrequest', - 'pullrequest:write', - 'pipeline', - 'pipeline:write', - 'webhook', - ], + scopes: [...OAUTH_SCOPES.bitbucket], }, }, defaultService: 'bitbucket', @@ -784,7 +477,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'notion-service-account', icon: NotionIcon, baseProviderIcon: NotionIcon, - scopes: [], + scopes: [...OAUTH_SCOPES.notion], }, }, defaultService: 'notion', @@ -800,7 +493,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'clickup-service-account', icon: ClickUpIcon, baseProviderIcon: ClickUpIcon, - scopes: [], + scopes: [...OAUTH_SCOPES.clickup], }, }, defaultService: 'clickup', @@ -816,7 +509,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'linear-service-account', icon: LinearIcon, baseProviderIcon: LinearIcon, - scopes: ['read', 'write'], + scopes: [...OAUTH_SCOPES.linear], }, }, defaultService: 'linear', @@ -832,40 +525,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'manageengine-sdp', icon: ManageEngineIcon, baseProviderIcon: ManageEngineIcon, - // ServiceDesk Plus Cloud scopes are `SDPOnDemand..` - // (getting-started/oauth-2.0.html). Enumerated per operation rather - // than requested as the broader `.ALL` group scopes, so the consent - // screen names exactly what the block can do. - // - // The five modules here are the ones the tools cover. Notably absent: - // the standalone Tasks module (/api/v3/tasks). Its endpoints are - // documented but the scope table publishes no `tasks` entry, and - // guessing one would put an unverified scope on every user's consent - // screen - so those tools are deliberately not implemented. - scopes: [ - 'SDPOnDemand.requests.CREATE', - 'SDPOnDemand.requests.READ', - 'SDPOnDemand.requests.UPDATE', - 'SDPOnDemand.requests.DELETE', - 'SDPOnDemand.problems.CREATE', - 'SDPOnDemand.problems.READ', - 'SDPOnDemand.problems.UPDATE', - 'SDPOnDemand.problems.DELETE', - 'SDPOnDemand.changes.CREATE', - 'SDPOnDemand.changes.READ', - 'SDPOnDemand.changes.UPDATE', - 'SDPOnDemand.changes.DELETE', - 'SDPOnDemand.assets.CREATE', - 'SDPOnDemand.assets.READ', - 'SDPOnDemand.assets.UPDATE', - 'SDPOnDemand.assets.DELETE', - 'SDPOnDemand.solutions.CREATE', - 'SDPOnDemand.solutions.READ', - 'SDPOnDemand.solutions.UPDATE', - 'SDPOnDemand.solutions.DELETE', - // Zoho account profile, used by getUserInfo to label the credential. - 'aaaserver.profile.READ', - ], + scopes: [...OAUTH_SCOPES['manageengine-sdp']], }, }, defaultService: 'manageengine-sdp', @@ -881,15 +541,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'monday-service-account', icon: MondayIcon, baseProviderIcon: MondayIcon, - scopes: [ - 'boards:read', - 'boards:write', - 'updates:read', - 'updates:write', - 'webhooks:read', - 'webhooks:write', - 'me:read', - ], + scopes: [...OAUTH_SCOPES.monday], }, }, defaultService: 'monday', @@ -904,7 +556,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'box', icon: BoxCompanyIcon, baseProviderIcon: BoxCompanyIcon, - scopes: ['root_readwrite', 'sign_requests.readwrite'], + scopes: [...OAUTH_SCOPES.box], serviceAccountProviderId: 'box-service-account', }, }, @@ -920,15 +572,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'dropbox', icon: DropboxIcon, baseProviderIcon: DropboxIcon, - scopes: [ - 'account_info.read', - 'files.metadata.read', - 'files.metadata.write', - 'files.content.read', - 'files.content.write', - 'sharing.read', - 'sharing.write', - ], + scopes: [...OAUTH_SCOPES.dropbox], }, }, defaultService: 'dropbox', @@ -944,14 +588,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'shopify-service-account', icon: ShopifyIcon, baseProviderIcon: ShopifyIcon, - scopes: [ - 'write_products', - 'write_orders', - 'write_customers', - 'write_inventory', - 'read_locations', - 'write_merchant_managed_fulfillment_orders', - ], + scopes: [...OAUTH_SCOPES.shopify], }, }, defaultService: 'shopify', @@ -967,28 +604,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'slack-custom-bot', icon: SlackIcon, baseProviderIcon: SlackIcon, - scopes: [ - 'channels:read', - 'channels:history', - 'channels:manage', - 'groups:read', - 'groups:history', - 'groups:write', - 'chat:write', - 'chat:write.public', - ...SLACK_APPROVAL_GATED_SCOPES, - 'im:write', - 'im:read', - 'users:read', - // TODO: Add 'users:read.email' once Slack app review is approved - 'files:write', - 'files:read', - 'canvases:read', - 'canvases:write', - 'reactions:write', - 'reactions:read', - // TODO: Add 'pins:read' once Slack app review is approved - ], + scopes: [...OAUTH_SCOPES.slack, ...SLACK_APPROVAL_GATED_SCOPES], }, }, defaultService: 'slack', @@ -1004,7 +620,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'snowflake-service-account', icon: SnowflakeIcon, baseProviderIcon: SnowflakeIcon, - scopes: [], + scopes: [...OAUTH_SCOPES.snowflake], authType: 'service_account', }, }, @@ -1022,7 +638,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'netsuite-service-account', icon: NetSuiteIcon, baseProviderIcon: NetSuiteIcon, - scopes: [], + scopes: [...OAUTH_SCOPES.netsuite], authType: 'service_account', }, }, @@ -1038,24 +654,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'reddit', icon: RedditIcon, baseProviderIcon: RedditIcon, - scopes: [ - 'identity', - 'read', - 'submit', - 'vote', - 'save', - 'edit', - 'subscribe', - 'history', - 'privatemessages', - 'account', - 'mysubreddits', - 'flair', - 'report', - 'modposts', - 'modflair', - 'modmail', - ], + scopes: [...OAUTH_SCOPES.reddit], }, }, defaultService: 'reddit', @@ -1071,7 +670,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'wealthbox-service-account', icon: WealthboxIcon, baseProviderIcon: WealthboxIcon, - scopes: ['login', 'data'], + scopes: [...OAUTH_SCOPES.wealthbox], }, }, defaultService: 'wealthbox', @@ -1087,7 +686,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'webflow-service-account', icon: WebflowIcon, baseProviderIcon: WebflowIcon, - scopes: ['cms:read', 'cms:write', 'sites:read', 'sites:write', 'forms:read'], + scopes: [...OAUTH_SCOPES.webflow], }, }, defaultService: 'webflow', @@ -1103,7 +702,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'trello-service-account', icon: TrelloIcon, baseProviderIcon: TrelloIcon, - scopes: ['read', 'write'], + scopes: [...OAUTH_SCOPES.trello], }, }, defaultService: 'trello', @@ -1119,7 +718,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'asana-service-account', icon: AsanaIcon, baseProviderIcon: AsanaIcon, - scopes: ['default'], + scopes: [...OAUTH_SCOPES.asana], }, }, defaultService: 'asana', @@ -1135,17 +734,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'attio-service-account', icon: AttioIcon, baseProviderIcon: AttioIcon, - scopes: [ - 'record_permission:read-write', - 'object_configuration:read-write', - 'list_configuration:read-write', - 'list_entry:read-write', - 'note:read-write', - 'task:read-write', - 'comment:read-write', - 'user_management:read', - 'webhook:read-write', - ], + scopes: [...OAUTH_SCOPES.attio], }, }, defaultService: 'attio', @@ -1161,7 +750,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'calcom-service-account', icon: CalComIcon, baseProviderIcon: CalComIcon, - scopes: [], + scopes: [...OAUTH_SCOPES.calcom], }, }, defaultService: 'calcom', @@ -1176,7 +765,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'docusign', icon: DocuSignIcon, baseProviderIcon: DocuSignIcon, - scopes: ['signature', 'extended'], + scopes: [...OAUTH_SCOPES.docusign], }, }, defaultService: 'docusign', @@ -1192,15 +781,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'pipedrive-service-account', icon: PipedriveIcon, baseProviderIcon: PipedriveIcon, - scopes: [ - 'base', - 'deals:full', - 'contacts:full', - 'leads:full', - 'activities:full', - 'mail:full', - 'projects:full', - ], + scopes: [...OAUTH_SCOPES.pipedrive], }, }, defaultService: 'pipedrive', @@ -1216,28 +797,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'hubspot-service-account', icon: HubspotIcon, baseProviderIcon: HubspotIcon, - scopes: [ - 'crm.objects.contacts.read', - 'crm.objects.contacts.write', - 'crm.objects.companies.read', - 'crm.objects.companies.write', - 'crm.objects.deals.read', - 'crm.objects.deals.write', - 'crm.objects.owners.read', - 'crm.objects.users.read', - 'crm.objects.marketing_events.read', - 'crm.objects.line_items.read', - 'crm.objects.line_items.write', - 'crm.objects.quotes.read', - 'crm.objects.appointments.read', - 'crm.objects.appointments.write', - 'crm.objects.carts.read', - 'sales-email-read', - 'crm.lists.read', - 'crm.lists.write', - 'tickets', - 'oauth', - ], + scopes: [...OAUTH_SCOPES.hubspot], }, }, defaultService: 'hubspot', @@ -1253,7 +813,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'harmonic-service-account', icon: HarmonicIcon, baseProviderIcon: HarmonicIcon, - scopes: [], + scopes: [...OAUTH_SCOPES.harmonic], authType: 'service_account', }, }, @@ -1269,7 +829,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'linkedin', icon: LinkedInIcon, baseProviderIcon: LinkedInIcon, - scopes: ['profile', 'openid', 'email', 'w_member_social'], + scopes: [...OAUTH_SCOPES.linkedin], }, }, defaultService: 'linkedin', @@ -1284,13 +844,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'instagram', icon: InstagramIcon, baseProviderIcon: InstagramIcon, - scopes: [ - 'instagram_business_basic', - 'instagram_business_content_publish', - 'instagram_business_manage_comments', - 'instagram_business_manage_messages', - 'instagram_business_manage_insights', - ], + scopes: [...OAUTH_SCOPES.instagram], }, }, defaultService: 'instagram', @@ -1309,7 +863,7 @@ export const OAUTH_PROVIDERS: Record = { serviceAccountProviderId: 'salesforce-service-account', icon: SalesforceIcon, baseProviderIcon: SalesforceIcon, - scopes: ['api', 'refresh_token', 'openid'], + scopes: [...OAUTH_SCOPES.salesforce], }, }, defaultService: 'salesforce', @@ -1339,31 +893,7 @@ export const OAUTH_PROVIDERS: Record = { // Desk.search.READ, Desk.webhooks.READ and Desk.webhooks.UPDATE were // requested but unused - no tool searches, and the provider never lists // or edits a subscription. - scopes: [ - // READ + UPDATE rather than tickets.ALL: no tool creates or deletes a - // ticket, and ALL additionally grants ticket DELETE. Threads, comments - // and attachments live under the tickets module and are covered by - // these two. NOTE: Zoho publishes no scope line for the attachment - // content sub-path - verify attachment download against a live account - // before merge and widen here if it returns SCOPE_MISMATCH. - 'Desk.tickets.READ', - 'Desk.tickets.UPDATE', - 'Desk.contacts.READ', - // READ only: the knowledge base connector syncs Help Center articles - // via GET /articles and GET /articles/{id}; nothing authors one. - 'Desk.articles.READ', - // GET /organizations documents `Desk.organization.READ , Desk.basic.READ`. - // Sibling endpoints spell the same construction "requires X and Y" - // (dependencyMappings, roles), so the comma is AND, not OR. - 'Desk.organization.READ', - // READ only: the agent picker for `assigneeId` lists agents, and no - // tool creates, edits or deletes one. - 'Desk.agents.READ', - 'Desk.basic.READ', - 'Desk.webhooks.CREATE', - 'Desk.webhooks.DELETE', - 'aaaserver.profile.READ', - ], + scopes: [...OAUTH_SCOPES['zoho-desk']], }, }, defaultService: 'zoho-desk', @@ -1378,19 +908,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'zoom', icon: ZoomIcon, baseProviderIcon: ZoomIcon, - scopes: [ - 'user:read:user', - 'meeting:write:meeting', - 'meeting:read:meeting', - 'meeting:read:list_meetings', - 'meeting:update:meeting', - 'meeting:delete:meeting', - 'meeting:read:invitation', - 'meeting:read:list_past_participants', - 'cloud_recording:read:list_user_recordings', - 'cloud_recording:read:list_recording_files', - 'cloud_recording:delete:recording_file', - ], + scopes: [...OAUTH_SCOPES.zoom], serviceAccountProviderId: 'zoom-service-account', }, }, @@ -1406,7 +924,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'wordpress', icon: WordpressIcon, baseProviderIcon: WordpressIcon, - scopes: ['global'], + scopes: [...OAUTH_SCOPES.wordpress], }, }, defaultService: 'wordpress', @@ -1421,25 +939,7 @@ export const OAUTH_PROVIDERS: Record = { providerId: 'spotify', icon: SpotifyIcon, baseProviderIcon: SpotifyIcon, - scopes: [ - 'user-read-private', - 'user-read-email', - 'user-library-read', - 'user-library-modify', - 'playlist-read-private', - 'playlist-read-collaborative', - 'playlist-modify-public', - 'playlist-modify-private', - 'user-read-playback-state', - 'user-modify-playback-state', - 'user-read-currently-playing', - 'user-read-recently-played', - 'user-top-read', - 'user-follow-read', - 'user-follow-modify', - 'user-read-playback-position', - 'ugc-image-upload', - ], + scopes: [...OAUTH_SCOPES.spotify], }, }, defaultService: 'spotify', diff --git a/apps/sim/lib/oauth/scopes.test.ts b/apps/sim/lib/oauth/scopes.test.ts new file mode 100644 index 00000000000..ee76eb1f695 --- /dev/null +++ b/apps/sim/lib/oauth/scopes.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, it } from 'vitest' +import { OAUTH_PROVIDERS } from './oauth' +import { getScopeDescription, OAUTH_SCOPES } from './scopes' + +describe('getScopeDescription', () => { + it.concurrent('uses provider-specific labels for Bitbucket scope names', () => { + expect(getScopeDescription('account', 'bitbucket')).toBe( + 'View your Bitbucket account and workspace memberships' + ) + expect(getScopeDescription('pipeline:write', 'bitbucket')).toBe('Run and stop pipelines') + expect(getScopeDescription('webhook', 'bitbucket')).toBe('Manage repository webhooks') + }) + + /** + * `read` and `write` are issued by Linear, Trello and Reddit alike, so the + * shared label cannot describe any of them precisely. Each provider that + * reuses the name gets its own. + */ + it.concurrent('disambiguates scope names more than one provider issues', () => { + expect(getScopeDescription('read', 'trello')).toBe( + 'View boards, lists, and cards you can access' + ) + expect(getScopeDescription('read', 'reddit')).toBe( + 'View posts, comments, and subreddits through your account' + ) + expect(getScopeDescription('read', 'linear')).toBe('Read access to connected account data') + }) + + it.concurrent('preserves the existing Reddit meaning of the account scope', () => { + expect(getScopeDescription('account', 'reddit')).toBe('Update account preferences and settings') + expect(getScopeDescription('account')).toBe('Update account preferences and settings') + }) + + /** + * The consent screen is where a user decides what to grant, so a write scope + * has to read as one. `w_member_social` previously said 'Access LinkedIn + * profile', describing a posting grant as a profile read. + * + * The wording tracks LinkedIn's own: "Post, comment, and like posts on behalf + * of an authenticated member." It names all three verbs even though Sim only + * posts -- the label describes the grant the token carries, not Sim's current + * use of it, and LinkedIn's scopes cannot be sub-selected. + */ + it.concurrent('describes w_member_social as the write grant it is', () => { + const description = getScopeDescription('w_member_social', 'linkedin') + + expect(description).toBe('Post, comment, and like posts on your behalf') + expect(description).not.toMatch(/access .*profile/i) + }) + + it.concurrent('leaves the read-only LinkedIn scopes read-only', () => { + expect(getScopeDescription('profile', 'linkedin')).toBe('Access profile information') + expect(getScopeDescription('email', 'linkedin')).toBe('Access email address') + }) +}) + +describe('OAUTH_SCOPES', () => { + /** + * The table lives apart from `OAUTH_PROVIDERS` so the docs generator can import + * it without pulling in React icons. That split is only safe while the two + * agree: a service whose scopes went missing would silently request nothing, + * and an orphaned entry would publish scopes no consent screen asks for. + */ + it.concurrent('covers exactly the services declared in OAUTH_PROVIDERS', () => { + const declared = Object.values(OAUTH_PROVIDERS) + .flatMap((provider) => Object.keys(provider.services)) + .sort() + + expect(Object.keys(OAUTH_SCOPES).sort()).toEqual(declared) + }) + + it.concurrent('requests every declared scope at authorization time', () => { + for (const [serviceId, scopes] of Object.entries(OAUTH_SCOPES)) { + const service = Object.values(OAUTH_PROVIDERS).find( + (provider) => provider.services[serviceId] + )?.services[serviceId] + + expect(service?.scopes, serviceId).toEqual(expect.arrayContaining([...scopes])) + } + }) +}) diff --git a/apps/sim/lib/oauth/scopes.ts b/apps/sim/lib/oauth/scopes.ts new file mode 100644 index 00000000000..bb8465b7d9d --- /dev/null +++ b/apps/sim/lib/oauth/scopes.ts @@ -0,0 +1,1252 @@ +import { SALESFORCE_ADDITIONAL_PROVIDER_IDS } from './salesforce' + +/** + * Canonical OAuth scope data: the scope set each service requests, and the + * human-readable label for an individual scope. + * + * Dependency-free on purpose, and it only ever imports modules that are too. + * Three callers need this data and no single environment suits all of them: + * `oauth.ts` pulls in React icons and `env`, and `scripts/generate-docs.ts` + * runs outside Next and can import neither. A module whose whole graph is free + * of them is the only shape all three can consume, which is what keeps the + * published `## Scopes` tables and the grant actually requested at + * authorization time from drifting apart. + * + * It is also the one module a build script reads OAuth data from. Where a + * provider module owns something the docs need, it is re-exported here rather + * than imported provider-by-provider, so the generator never learns any + * individual provider's name. + */ + +/** + * Scopes requested for each OAuth service, keyed by service id. + * + * Read by {@link OAUTH_PROVIDERS} to build each service's authorization request + * and by the docs generator to emit every integration page's `## Scopes` + * section, so a scope added here reaches both the consent screen and the + * published docs. Slack's approval-gated extras are the one exception: they are + * appended in `oauth.ts` because whether they are requested depends on an + * environment flag, and generated docs must not vary by deployment. + */ +export const OAUTH_SCOPES = { + 'claude-platform': [], + gmail: [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/gmail.send', + 'https://www.googleapis.com/auth/gmail.modify', + 'https://www.googleapis.com/auth/gmail.labels', + ], + 'google-drive': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/drive', + ], + 'google-docs': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/drive', + ], + 'google-sheets': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/drive', + ], + 'google-forms': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/drive', + 'https://www.googleapis.com/auth/forms.body', + 'https://www.googleapis.com/auth/forms.responses.readonly', + ], + 'google-calendar': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/calendar', + ], + 'google-contacts': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/contacts', + ], + 'google-ads': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/adwords', + ], + 'google-bigquery': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/bigquery', + ], + 'google-tasks': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/tasks', + ], + 'google-vault': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/ediscovery', + // Least-privilege scope for read-only consumers. The knowledge base + // connector only lists matters, holds, and saved queries, all of which + // accept ediscovery.readonly; the block's export tools still need the + // read-write scope above. + 'https://www.googleapis.com/auth/ediscovery.readonly', + 'https://www.googleapis.com/auth/devstorage.read_only', + ], + 'google-groups': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/admin.directory.group', + 'https://www.googleapis.com/auth/admin.directory.group.member', + ], + 'google-chat': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/chat.spaces.readonly', + 'https://www.googleapis.com/auth/chat.messages.readonly', + ], + 'google-meet': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/meetings.space.created', + 'https://www.googleapis.com/auth/meetings.space.readonly', + ], + 'google-service-account': [], + 'vertex-ai': [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/cloud-platform', + ], + 'microsoft-ad': [ + 'openid', + 'profile', + 'email', + 'User.ReadWrite.All', + 'Group.ReadWrite.All', + 'GroupMember.ReadWrite.All', + 'LicenseAssignment.Read.All', + 'LicenseAssignment.ReadWrite.All', + 'UserAuthenticationMethod.ReadWrite.All', + 'AuditLog.Read.All', + 'Application.Read.All', + 'AppRoleAssignment.ReadWrite.All', + 'RoleManagement.ReadWrite.Directory', + 'Device.Read.All', + 'Policy.Read.All', + 'offline_access', + ], + 'microsoft-dataverse': [ + 'openid', + 'profile', + 'email', + 'https://dynamics.microsoft.com/user_impersonation', + 'offline_access', + ], + /** + * Workbooks are ordinary drive items, so the integration reads and writes them + * through the Files permissions rather than an Excel-specific scope. Microsoft + * Graph exposes no Excel permission of its own. + * + * The `.All` variants and `Sites.Read.All` are what make the block's SharePoint + * file picker work. `Files.ReadWrite` alone covers only the signed-in user's own + * OneDrive, so a workbook in a document library is rejected for insufficient + * privileges, and the drive picker's `GET /sites/{id}/drives` call needs the + * Sites read. All four are user-consentable, so this does not push the + * integration behind admin consent, and none of them grants access to anything + * the signed-in account could not already open. + * + * Microsoft only grants newly-added scopes on a fresh authorization, so accounts + * connected before these existed must reconnect before a SharePoint-hosted + * workbook will open. + * + * @see https://learn.microsoft.com/en-us/graph/permissions-reference + */ + 'microsoft-excel': [ + 'openid', + 'profile', + 'email', + 'Files.Read', + 'Files.ReadWrite', + 'Files.Read.All', + 'Files.ReadWrite.All', + 'Sites.Read.All', + 'offline_access', + ], + 'microsoft-planner': [ + 'openid', + 'profile', + 'email', + 'Group.ReadWrite.All', + 'Group.Read.All', + 'Tasks.ReadWrite', + 'offline_access', + ], + 'microsoft-teams': [ + 'openid', + 'profile', + 'email', + 'User.Read', + 'Chat.Read', + 'Chat.ReadWrite', + 'Chat.ReadBasic', + 'ChatMessage.Send', + 'Channel.ReadBasic.All', + 'ChannelMessage.Send', + 'ChannelMessage.Read.All', + 'ChannelMessage.ReadWrite', + 'ChannelMember.Read.All', + 'Group.Read.All', + 'Group.ReadWrite.All', + 'Team.ReadBasic.All', + 'TeamMember.Read.All', + 'offline_access', + 'Files.Read', + 'Sites.Read.All', + ], + /** + * Word documents are ordinary drive items, so the integration reads and + * writes them through the Files permissions rather than a Word-specific + * scope — Microsoft Graph exposes no Word API of its own. + * + * The `.All` variants are what make the SharePoint drive the block + * exposes actually work: `Files.ReadWrite` alone covers only the signed-in + * user's own OneDrive, so a document library would be rejected for + * insufficient privileges. Both are user-consentable, so this does not + * push the integration behind admin consent, and neither grants access to + * anything the signed-in account could not already open. + * + * @see https://learn.microsoft.com/en-us/graph/permissions-reference + */ + 'microsoft-word': [ + 'openid', + 'profile', + 'email', + 'Files.Read', + 'Files.ReadWrite', + 'Files.Read.All', + 'Files.ReadWrite.All', + 'offline_access', + ], + /** + * `Calendars.ReadWrite` backs the Outlook calendar operations. Graph documents it + * as the sole accepted permission for creating and updating events and for + * accept / tentativelyAccept / decline ("Higher: Not available"), and it is + * supported for both work/school and personal Microsoft accounts. + * + * Do NOT add `Calendars.ReadWrite.Shared` here. This provider is shared by work + * and personal Outlook accounts, and the `.Shared` calendar scopes are not + * confirmed supported for personal Microsoft accounts — requesting one risks + * failing consent for personal users, which would take mail access down with it. + * That is the same reasoning that kept `findMeetingTimes` out of this integration. + * The consequence is that calendar operations target calendars the account owns; + * picking a calendar shared by another user may return 403 from Graph. + * + * Microsoft only grants newly-added scopes on a fresh authorization, so users who + * connected Outlook before `Calendars.ReadWrite` existed must reconnect + * (re-consent) before the calendar operations will work. + * + * @see https://learn.microsoft.com/en-us/graph/permissions-reference + */ + outlook: [ + 'openid', + 'profile', + 'email', + 'Mail.ReadWrite', + 'Mail.ReadBasic', + 'Mail.Read', + 'Mail.Send', + 'Calendars.ReadWrite', + 'offline_access', + ], + onedrive: ['openid', 'profile', 'email', 'Files.Read', 'Files.ReadWrite', 'offline_access'], + sharepoint: [ + 'openid', + 'profile', + 'email', + 'Sites.Read.All', + 'Sites.ReadWrite.All', + 'Sites.Manage.All', + 'offline_access', + ], + x: [ + 'tweet.read', + 'tweet.write', + 'tweet.moderate.write', + 'users.read', + 'follows.read', + 'follows.write', + 'bookmark.read', + 'bookmark.write', + 'like.read', + 'like.write', + 'block.read', + 'block.write', + 'mute.read', + 'mute.write', + 'offline.access', + ], + tiktok: ['user.info.basic', 'user.info.profile', 'user.info.stats', 'video.upload', 'video.list'], + 'atlassian-service-account': [], + confluence: [ + 'read:confluence-content.all', + 'read:confluence-space.summary', + 'read:space:confluence', + 'write:confluence-content', + 'write:confluence-space', + 'write:confluence-file', + 'read:page:confluence', + 'write:page:confluence', + 'read:comment:confluence', + 'write:comment:confluence', + 'delete:comment:confluence', + 'delete:attachment:confluence', + 'delete:page:confluence', + 'read:label:confluence', + 'write:label:confluence', + 'read:attachment:confluence', + 'write:attachment:confluence', + 'search:confluence', + 'read:me', + 'offline_access', + 'read:hierarchical-content:confluence', + 'read:content.metadata:confluence', + 'read:user:confluence', + 'read:confluence-user', + 'read:task:confluence', + 'write:task:confluence', + 'write:space:confluence', + 'delete:space:confluence', + 'read:blogpost:confluence', + 'write:blogpost:confluence', + 'delete:blogpost:confluence', + 'read:content.property:confluence', + 'write:content.property:confluence', + 'read:space.property:confluence', + 'write:space.property:confluence', + 'read:space.permission:confluence', + ], + jira: [ + 'read:jira-user', + 'read:jira-work', + 'write:jira-work', + 'read:me', + 'offline_access', + 'read:issue.vote:jira', + 'read:user:jira', + 'delete:issue:jira', + 'delete:comment:jira', + 'delete:attachment:jira', + 'delete:issue-worklog:jira', + 'delete:issue-link:jira', + // Jira Service Management scopes. The classic scopes are required: Atlassian + // enforces an endpoint's granular scope set as all-of, and several JSM request + // endpoints include scopes outside this list in their granular sets. + 'read:servicedesk-request', + 'write:servicedesk-request', + 'manage:servicedesk-customer', + 'read:servicedesk:jira-service-management', + 'read:requesttype:jira-service-management', + 'read:request:jira-service-management', + 'write:request:jira-service-management', + 'read:request.comment:jira-service-management', + 'write:request.comment:jira-service-management', + 'read:servicedesk.customer:jira-service-management', + 'write:servicedesk.customer:jira-service-management', + 'read:organization:jira-service-management', + 'write:organization:jira-service-management', + 'read:servicedesk.organization:jira-service-management', + 'write:servicedesk.organization:jira-service-management', + 'read:queue:jira-service-management', + 'read:request.sla:jira-service-management', + 'read:request.status:jira-service-management', + 'write:request.status:jira-service-management', + 'read:request.participant:jira-service-management', + 'write:request.participant:jira-service-management', + 'read:request.approval:jira-service-management', + 'write:request.approval:jira-service-management', + 'read:cmdb-object:jira', + 'write:cmdb-object:jira', + 'delete:cmdb-object:jira', + 'read:cmdb-schema:jira', + 'read:cmdb-type:jira', + 'read:cmdb-attribute:jira', + ], + airtable: [ + 'data.records:read', + 'data.records:write', + 'schema.bases:read', + 'user.email:read', + 'webhook:manage', + ], + bitbucket: [ + 'account', + 'repository', + 'repository:write', + 'pullrequest', + 'pullrequest:write', + 'pipeline', + 'pipeline:write', + 'webhook', + ], + notion: [], + clickup: [], + linear: ['read', 'write'], + monday: [ + 'boards:read', + 'boards:write', + 'updates:read', + 'updates:write', + 'webhooks:read', + 'webhooks:write', + 'me:read', + ], + box: ['root_readwrite', 'sign_requests.readwrite'], + dropbox: [ + 'account_info.read', + 'files.metadata.read', + 'files.metadata.write', + 'files.content.read', + 'files.content.write', + 'sharing.read', + 'sharing.write', + ], + shopify: [ + 'write_products', + 'write_orders', + 'write_customers', + 'write_inventory', + 'read_locations', + 'write_merchant_managed_fulfillment_orders', + ], + slack: [ + 'channels:read', + 'channels:history', + 'channels:manage', + 'groups:read', + 'groups:history', + 'groups:write', + 'chat:write', + 'chat:write.public', + 'im:write', + 'im:read', + 'users:read', + // TODO: Add 'users:read.email' once Slack app review is approved + 'files:write', + 'files:read', + 'canvases:read', + 'canvases:write', + 'reactions:write', + 'reactions:read', + // TODO: Add 'pins:read' once Slack app review is approved + ], + snowflake: [], + netsuite: [], + reddit: [ + 'identity', + 'read', + 'submit', + 'vote', + 'save', + 'edit', + 'subscribe', + 'history', + 'privatemessages', + 'account', + 'mysubreddits', + 'flair', + 'report', + 'modposts', + 'modflair', + 'modmail', + ], + wealthbox: ['login', 'data'], + webflow: ['cms:read', 'cms:write', 'sites:read', 'sites:write', 'forms:read'], + trello: ['read', 'write'], + asana: ['default'], + attio: [ + 'record_permission:read-write', + 'object_configuration:read-write', + 'list_configuration:read-write', + 'list_entry:read-write', + 'note:read-write', + 'task:read-write', + 'comment:read-write', + 'user_management:read', + 'webhook:read-write', + ], + calcom: [], + docusign: ['signature', 'extended'], + pipedrive: [ + 'base', + 'deals:full', + 'contacts:full', + 'leads:full', + 'activities:full', + 'mail:full', + 'projects:full', + ], + hubspot: [ + 'crm.objects.contacts.read', + 'crm.objects.contacts.write', + 'crm.objects.companies.read', + 'crm.objects.companies.write', + 'crm.objects.deals.read', + 'crm.objects.deals.write', + 'crm.objects.owners.read', + 'crm.objects.users.read', + 'crm.objects.marketing_events.read', + 'crm.objects.line_items.read', + 'crm.objects.line_items.write', + 'crm.objects.quotes.read', + 'crm.objects.appointments.read', + 'crm.objects.appointments.write', + 'crm.objects.carts.read', + 'sales-email-read', + 'crm.lists.read', + 'crm.lists.write', + 'tickets', + 'oauth', + ], + harmonic: [], + linkedin: ['profile', 'openid', 'email', 'w_member_social'], + instagram: [ + 'instagram_business_basic', + 'instagram_business_content_publish', + 'instagram_business_manage_comments', + 'instagram_business_manage_messages', + 'instagram_business_manage_insights', + ], + salesforce: ['api', 'refresh_token', 'openid'], + /** + * ServiceDesk Plus Cloud scopes are `SDPOnDemand..`, + * enumerated per operation rather than requested as the broader `.ALL` group + * scopes, so the consent screen names exactly what the block can do. + */ + 'manageengine-sdp': [ + 'SDPOnDemand.requests.CREATE', + 'SDPOnDemand.requests.READ', + 'SDPOnDemand.requests.UPDATE', + 'SDPOnDemand.requests.DELETE', + 'SDPOnDemand.problems.CREATE', + 'SDPOnDemand.problems.READ', + 'SDPOnDemand.problems.UPDATE', + 'SDPOnDemand.problems.DELETE', + 'SDPOnDemand.changes.CREATE', + 'SDPOnDemand.changes.READ', + 'SDPOnDemand.changes.UPDATE', + 'SDPOnDemand.changes.DELETE', + 'SDPOnDemand.assets.CREATE', + 'SDPOnDemand.assets.READ', + 'SDPOnDemand.assets.UPDATE', + 'SDPOnDemand.assets.DELETE', + 'SDPOnDemand.solutions.CREATE', + 'SDPOnDemand.solutions.READ', + 'SDPOnDemand.solutions.UPDATE', + 'SDPOnDemand.solutions.DELETE', + // Zoho account profile, used by getUserInfo to label the credential. + 'aaaserver.profile.READ', + ], + 'zoho-desk': [ + // READ + UPDATE rather than tickets.ALL: no tool creates or deletes a + // ticket, and ALL additionally grants ticket DELETE. Threads, comments + // and attachments live under the tickets module and are covered by + // these two. NOTE: Zoho publishes no scope line for the attachment + // content sub-path - verify attachment download against a live account + // before merge and widen here if it returns SCOPE_MISMATCH. + 'Desk.tickets.READ', + 'Desk.tickets.UPDATE', + 'Desk.contacts.READ', + // READ only: the knowledge base connector syncs Help Center articles + // via GET /articles and GET /articles/{id}; nothing authors one. + 'Desk.articles.READ', + // GET /organizations documents `Desk.organization.READ , Desk.basic.READ`. + // Sibling endpoints spell the same construction "requires X and Y" + // (dependencyMappings, roles), so the comma is AND, not OR. + 'Desk.organization.READ', + // READ only: the agent picker for `assigneeId` lists agents, and no + // tool creates, edits or deletes one. + 'Desk.agents.READ', + 'Desk.basic.READ', + 'Desk.webhooks.CREATE', + 'Desk.webhooks.DELETE', + 'aaaserver.profile.READ', + ], + zoom: [ + 'user:read:user', + 'meeting:write:meeting', + 'meeting:read:meeting', + 'meeting:read:list_meetings', + 'meeting:update:meeting', + 'meeting:delete:meeting', + 'meeting:read:invitation', + 'meeting:read:list_past_participants', + 'cloud_recording:read:list_user_recordings', + 'cloud_recording:read:list_recording_files', + 'cloud_recording:delete:recording_file', + ], + wordpress: ['global'], + spotify: [ + 'user-read-private', + 'user-read-email', + 'user-library-read', + 'user-library-modify', + 'playlist-read-private', + 'playlist-read-collaborative', + 'playlist-modify-public', + 'playlist-modify-private', + 'user-read-playback-state', + 'user-modify-playback-state', + 'user-read-currently-playing', + 'user-read-recently-played', + 'user-top-read', + 'user-follow-read', + 'user-follow-modify', + 'user-read-playback-position', + 'ugc-image-upload', + ], +} as const satisfies Record + +/** Service ids that declare a scope set. */ +export type OAuthScopedService = keyof typeof OAUTH_SCOPES + +/** + * Further provider ids a service authenticates through, keyed by service id. + * + * A provider running more than one authorization server needs a separate + * connector registration per host, and each registration is its own redirect + * URI. Documenting only the primary sends a self-hoster off to register half of + * what they need. + * + * Derived from the provider module rather than restated here: Salesforce's + * `SALESFORCE_AUTH_SERVERS` is already the single source for its registrations, + * refresh endpoints and environment-picker labels, and a second copy of that + * list would be the one that goes stale. + */ +export const ADDITIONAL_PROVIDER_IDS = { + salesforce: SALESFORCE_ADDITIONAL_PROVIDER_IDS, +} as const satisfies Record + +/** + * Provider ids whose managed connector is the Google one. + * + * Lives here because the docs need it and `managed-oauth.ts` cannot be imported + * outside Next, and it is the single source both read: the connector factory + * matches against this set, so a Google service added to one is added to both. + */ +export const GOOGLE_MANAGED_OAUTH_PROVIDER_IDS: ReadonlySet = new Set([ + 'google-email', + 'google-calendar', + 'google-drive', + 'google-docs', + 'google-forms', + 'google-chat', + 'google-meet', + 'google-sheets', +]) + +/** + * Scopes a managed connection requests on top of the service's declared set, + * keyed by provider id. + * + * A managed connector unions `getCanonicalScopesForProvider` with its own + * `additionalScopes`, so a table printed from the declared set alone understates + * what that connection asks for. Google's adds `openid`, which is how the + * connector verifies the identity behind the credential. + */ +export const MANAGED_OAUTH_ADDITIONAL_SCOPES: Readonly> = + Object.fromEntries( + [...GOOGLE_MANAGED_OAUTH_PROVIDER_IDS].map((providerId) => [providerId, ['openid']]) + ) + +/** + * A caveat the scope rows of one service cannot carry on their own, keyed by + * service id. + * + * Dataverse is the case that forced this: the row below is the resource scope + * the connector declares, but binding an environment swaps it for that + * environment's `/.default`, so a table printed without the caveat claims to + * list a request the runtime does not make. + */ +export const SERVICE_SCOPE_NOTES = { + 'microsoft-dataverse': + "Connecting a specific environment requests that environment's `/.default` scope instead " + + 'of the resource scope above. The host is normalized to its `.api` form first, so ' + + '`https://contoso.crm.dynamics.com` is requested as ' + + '`https://contoso.api.crm.dynamics.com/.default`.', +} as const satisfies Record + +/** + * Scopes a service requests only where the deployment has opted in, keyed by + * service id. + * + * Held apart from {@link OAUTH_SCOPES} because a generated page must not vary + * by deployment, and published separately because a self-hoster who sets the + * flag still has to add these to the app before anyone can connect. Slack is + * the sharp case: it rejects the entire authorization with "unapproved + * permissions requested" if the app is not approved for one of them, so a + * reader who enables the flag without adding them loses Slack altogether. + */ +export const ENV_GATED_SCOPES = { + slack: { + envVar: 'SLACK_EXTENDED_SCOPES', + scopes: ['assistant:write', 'app_mentions:read', 'im:history'], + }, +} as const satisfies Record + +/** + * Centralized human-readable descriptions for OAuth scopes. + * Used by the OAuth Required Modal and available for any UI that needs to display scope info. + */ +export const SCOPE_DESCRIPTIONS: Record = { + // Zoho Desk scopes + 'Desk.tickets.READ': 'View tickets, threads, comments, and attachments', + 'Desk.tickets.UPDATE': 'Update tickets and add comments', + 'Desk.contacts.READ': 'View contacts', + 'Desk.articles.READ': 'View Help Center articles', + 'Desk.organization.READ': 'View organization details', + 'Desk.agents.READ': 'View agents', + 'Desk.basic.READ': 'View basic account and organization data', + 'Desk.webhooks.CREATE': 'Create webhooks', + 'Desk.webhooks.DELETE': 'Delete webhooks', + 'aaaserver.profile.READ': 'View your Zoho profile', + // ManageEngine ServiceDesk Plus Cloud scopes + 'SDPOnDemand.requests.CREATE': 'Create requests', + 'SDPOnDemand.requests.READ': 'View requests and their notes', + 'SDPOnDemand.requests.UPDATE': 'Update requests and add notes', + 'SDPOnDemand.requests.DELETE': 'Delete requests', + 'SDPOnDemand.problems.CREATE': 'Create problems', + 'SDPOnDemand.problems.READ': 'View problems and their notes', + 'SDPOnDemand.problems.UPDATE': 'Update problems and add notes', + 'SDPOnDemand.problems.DELETE': 'Delete problems', + 'SDPOnDemand.changes.CREATE': 'Create changes', + 'SDPOnDemand.changes.READ': 'View changes and their notes', + 'SDPOnDemand.changes.UPDATE': 'Update changes and add notes', + 'SDPOnDemand.changes.DELETE': 'Delete changes', + 'SDPOnDemand.assets.CREATE': 'Create assets', + 'SDPOnDemand.assets.READ': 'View assets', + 'SDPOnDemand.assets.UPDATE': 'Update assets', + 'SDPOnDemand.assets.DELETE': 'Delete assets', + 'SDPOnDemand.solutions.CREATE': 'Create knowledge base solutions', + 'SDPOnDemand.solutions.READ': 'View knowledge base solutions', + 'SDPOnDemand.solutions.UPDATE': 'Update knowledge base solutions', + 'SDPOnDemand.solutions.DELETE': 'Delete knowledge base solutions', + // Google scopes + 'https://www.googleapis.com/auth/gmail.send': 'Send emails', + 'https://www.googleapis.com/auth/gmail.labels': 'View and manage email labels', + 'https://www.googleapis.com/auth/gmail.readonly': 'View email messages and settings', + 'https://www.googleapis.com/auth/gmail.modify': 'View and manage email messages', + 'https://www.googleapis.com/auth/drive.file': 'View and manage Google Drive files', + 'https://www.googleapis.com/auth/drive': 'Access all Google Drive files', + 'https://www.googleapis.com/auth/calendar.readonly': 'View calendars and events', + 'https://www.googleapis.com/auth/calendar': 'View and manage calendar', + 'https://www.googleapis.com/auth/contacts': 'View and manage Google Contacts', + 'https://www.googleapis.com/auth/tasks': 'Create, read, update, and delete Google Tasks', + 'https://www.googleapis.com/auth/userinfo.email': 'View email address', + 'https://www.googleapis.com/auth/userinfo.profile': 'View basic profile info', + 'https://www.googleapis.com/auth/forms.body': 'View and manage Google Forms', + 'https://www.googleapis.com/auth/forms.responses.readonly': 'View responses to Google Forms', + 'https://www.googleapis.com/auth/adwords': 'Manage Google Ads campaigns and reporting', + 'https://www.googleapis.com/auth/bigquery': 'View and manage data in Google BigQuery', + 'https://www.googleapis.com/auth/ediscovery': 'Access Google Vault for eDiscovery', + 'https://www.googleapis.com/auth/ediscovery.readonly': + 'View Google Vault matters, holds, and saved queries', + 'https://www.googleapis.com/auth/devstorage.read_only': 'Read files from Google Cloud Storage', + 'https://www.googleapis.com/auth/admin.directory.group': 'Manage Google Workspace groups', + 'https://www.googleapis.com/auth/admin.directory.group.member': + 'Manage Google Workspace group memberships', + 'https://www.googleapis.com/auth/admin.directory.group.readonly': 'View Google Workspace groups', + 'https://www.googleapis.com/auth/admin.directory.group.member.readonly': + 'View Google Workspace group memberships', + 'https://www.googleapis.com/auth/chat.spaces.readonly': + 'View Google Chat spaces you are a member of', + 'https://www.googleapis.com/auth/chat.messages.readonly': + 'View messages in Google Chat spaces you are a member of', + 'https://www.googleapis.com/auth/meetings.space.created': + 'Create Google Meet spaces and manage the ones created through Sim', + 'https://www.googleapis.com/auth/meetings.space.readonly': + 'View Google Meet meeting space details', + 'https://www.googleapis.com/auth/cloud-platform': + 'Full access to Google Cloud resources for Vertex AI', + + // Confluence scopes + 'read:confluence-content.all': 'Read all Confluence content', + 'read:confluence-space.summary': 'Read Confluence space information', + 'read:space:confluence': 'View Confluence spaces', + 'read:space-details:confluence': 'View detailed Confluence space information', + 'write:confluence-content': 'Create and edit Confluence pages', + 'write:confluence-space': 'Manage Confluence spaces', + 'write:confluence-file': 'Upload files to Confluence', + 'read:content:confluence': 'Read Confluence content', + 'read:page:confluence': 'View Confluence pages', + 'write:page:confluence': 'Create and update Confluence pages', + 'read:comment:confluence': 'View comments on Confluence pages', + 'write:comment:confluence': 'Create and update comments', + 'delete:comment:confluence': 'Delete comments from Confluence pages', + 'read:attachment:confluence': 'View attachments on Confluence pages', + 'write:attachment:confluence': 'Upload and manage attachments', + 'delete:attachment:confluence': 'Delete attachments from Confluence pages', + 'delete:page:confluence': 'Delete Confluence pages', + 'read:label:confluence': 'View labels on Confluence content', + 'write:label:confluence': 'Add and remove labels', + 'search:confluence': 'Search Confluence content', + 'readonly:content.attachment:confluence': 'View attachments', + 'read:blogpost:confluence': 'View Confluence blog posts', + 'write:blogpost:confluence': 'Create and update Confluence blog posts', + 'read:content.property:confluence': 'View properties on Confluence content', + 'write:content.property:confluence': 'Create and manage content properties', + 'read:hierarchical-content:confluence': 'View page hierarchy (children and ancestors)', + 'read:content.metadata:confluence': 'View content metadata (required for ancestors)', + 'read:user:confluence': 'View Confluence user profiles', + 'read:confluence-user': 'View Confluence user profiles (v1 API)', + 'read:task:confluence': 'View Confluence inline tasks', + 'write:task:confluence': 'Update Confluence inline tasks', + 'delete:blogpost:confluence': 'Delete Confluence blog posts', + 'write:space:confluence': 'Create and update Confluence spaces', + 'delete:space:confluence': 'Delete Confluence spaces', + 'read:space.property:confluence': 'View Confluence space properties', + 'write:space.property:confluence': 'Create and manage space properties', + 'read:space.permission:confluence': 'View Confluence space permissions', + + // Common scopes + 'read:me': 'Read profile information', + offline_access: 'Access account when not using the application', + openid: 'Standard authentication', + profile: 'Access profile information', + email: 'Access email address', + + // Notion scopes + 'database.read': 'Read database', + 'database.write': 'Write to database', + 'projects.read': 'Read projects', + 'page.read': 'Read Notion pages', + 'page.write': 'Write to Notion pages', + 'workspace.content': 'Read Notion content', + 'workspace.name': 'Read Notion workspace name', + 'workspace.read': 'Read Notion workspace', + 'workspace.write': 'Write to Notion workspace', + 'user.email:read': 'Read email address', + + // GitHub scopes + repo: 'Access repositories', + workflow: 'Manage repository workflows', + 'read:user': 'Read public user information', + 'user:email': 'Access email address', + + // X (Twitter) scopes + 'tweet.read': 'Read tweets and timeline', + 'tweet.write': 'Post and delete tweets', + 'tweet.moderate.write': 'Hide and unhide replies to tweets', + 'users.read': 'Read user profiles and account information', + 'follows.read': 'View followers and following lists', + 'follows.write': 'Follow and unfollow users', + 'bookmark.read': 'View bookmarked tweets', + 'bookmark.write': 'Add and remove bookmarks', + 'like.read': 'View liked tweets and liking users', + 'like.write': 'Like and unlike tweets', + 'block.read': 'View blocked users', + 'block.write': 'Block and unblock users', + 'mute.read': 'View muted users', + 'mute.write': 'Mute and unmute users', + 'offline.access': 'Access account when not using the application', + + // TikTok scopes + 'user.info.basic': "Read a user's profile info (open id, avatar, display name)", + 'user.info.profile': "Read a user's profile info (bio, verification status, username)", + 'user.info.stats': "Read a user's stats (follower, following, likes, and video counts)", + 'video.upload': "Share content to a creator's account as a draft for further edit and post", + 'video.list': "Read a user's public TikTok videos", + + // Airtable scopes + 'data.records:read': 'Read records', + 'data.records:write': 'Write to records', + 'schema.bases:read': 'View bases and tables', + 'webhook:manage': 'Manage webhooks', + + // Jira scopes + 'read:jira-user': 'Read Jira user', + 'read:jira-work': 'Read Jira work', + 'write:jira-work': 'Write to Jira work', + 'manage:jira-webhook': 'Register and manage Jira webhooks', + 'read:webhook:jira': 'View Jira webhooks', + 'write:webhook:jira': 'Create and update Jira webhooks', + 'delete:webhook:jira': 'Delete Jira webhooks', + 'read:issue-event:jira': 'Read Jira issue events', + 'write:issue:jira': 'Write to Jira issues', + 'read:project:jira': 'Read Jira projects', + 'read:issue-type:jira': 'Read Jira issue types', + 'read:issue-meta:jira': 'Read Jira issue meta', + 'read:issue-security-level:jira': 'Read Jira issue security level', + 'read:issue.vote:jira': 'Read Jira issue votes', + 'read:issue.changelog:jira': 'Read Jira issue changelog', + 'read:avatar:jira': 'Read Jira avatar', + 'read:issue:jira': 'Read Jira issues', + 'read:status:jira': 'Read Jira status', + 'read:user:jira': 'Read Jira user', + 'read:field-configuration:jira': 'Read Jira field configuration', + 'read:issue-details:jira': 'Read Jira issue details', + 'read:field:jira': 'Read Jira field configurations', + 'read:jql:jira': 'Use JQL to filter Jira issues', + 'read:comment.property:jira': 'Read Jira comment properties', + 'read:issue.property:jira': 'Read Jira issue properties', + 'delete:issue:jira': 'Delete Jira issues', + 'write:comment:jira': 'Add and update comments on Jira issues', + 'read:comment:jira': 'Read comments on Jira issues', + 'delete:comment:jira': 'Delete comments from Jira issues', + 'read:attachment:jira': 'Read attachments from Jira issues', + 'write:attachment:jira': 'Add attachments to Jira issues', + 'delete:attachment:jira': 'Delete attachments from Jira issues', + 'write:issue-worklog:jira': 'Add and update worklog entries on Jira issues', + 'read:issue-worklog:jira': 'Read worklog entries from Jira issues', + 'delete:issue-worklog:jira': 'Delete worklog entries from Jira issues', + 'write:issue-link:jira': 'Create links between Jira issues', + 'delete:issue-link:jira': 'Delete links between Jira issues', + + // Jira Service Management scopes + 'read:servicedesk-request': 'View service desk requests', + 'write:servicedesk-request': 'Create and update service desk requests', + 'manage:servicedesk-customer': 'Manage service desk customers and organizations', + 'read:servicedesk:jira-service-management': 'View service desks and their settings', + 'read:requesttype:jira-service-management': 'View request types available in service desks', + 'read:request:jira-service-management': 'View customer requests in service desks', + 'write:request:jira-service-management': 'Create customer requests in service desks', + 'read:request.comment:jira-service-management': 'View comments on customer requests', + 'write:request.comment:jira-service-management': 'Add comments to customer requests', + 'read:customer:jira-service-management': 'View customer information', + 'write:customer:jira-service-management': 'Create and manage customers', + 'read:servicedesk.customer:jira-service-management': 'View customers linked to service desks', + 'write:servicedesk.customer:jira-service-management': + 'Add and remove customers from service desks', + 'read:organization:jira-service-management': 'View organizations', + 'write:organization:jira-service-management': 'Create and manage organizations', + 'read:servicedesk.organization:jira-service-management': + 'View organizations linked to service desks', + 'write:servicedesk.organization:jira-service-management': + 'Add and remove organizations from service desks', + 'read:organization.user:jira-service-management': 'View users in organizations', + 'write:organization.user:jira-service-management': 'Add and remove users from organizations', + 'read:organization.property:jira-service-management': 'View organization properties', + 'write:organization.property:jira-service-management': + 'Create and manage organization properties', + 'read:organization.profile:jira-service-management': 'View organization profiles', + 'write:organization.profile:jira-service-management': 'Update organization profiles', + 'read:queue:jira-service-management': 'View service desk queues and their issues', + 'read:request.sla:jira-service-management': 'View SLA information for customer requests', + 'read:request.status:jira-service-management': 'View status of customer requests', + 'write:request.status:jira-service-management': 'Transition customer request status', + 'read:request.participant:jira-service-management': 'View participants on customer requests', + 'write:request.participant:jira-service-management': + 'Add and remove participants from customer requests', + 'read:request.approval:jira-service-management': 'View approvals on customer requests', + 'write:request.approval:jira-service-management': 'Approve or decline customer requests', + 'read:cmdb-object:jira': 'View Assets objects and run AQL searches', + 'write:cmdb-object:jira': 'Create and update Assets objects', + 'delete:cmdb-object:jira': 'Delete Assets objects', + 'read:cmdb-schema:jira': 'View Assets object schemas', + 'read:cmdb-type:jira': 'View Assets object types', + 'read:cmdb-attribute:jira': 'View Assets object type attributes', + + // Microsoft scopes + 'User.Read': 'Read Microsoft user', + 'Chat.Read': 'Read Microsoft chats', + 'Chat.ReadWrite': 'Read and write Microsoft chats', + 'Chat.ReadBasic': 'Read Microsoft chats', + 'ChatMessage.Send': 'Send chat messages', + 'Channel.ReadBasic.All': 'Read Microsoft channels', + 'ChannelMessage.Send': 'Send channel messages', + 'ChannelMessage.Read.All': 'Read Microsoft channels', + 'ChannelMessage.ReadWrite': 'Read and write to Microsoft channels', + 'ChannelMember.Read.All': 'Read team channel members', + 'Group.Read.All': 'Read Microsoft groups', + 'Group.ReadWrite.All': 'Read and write all groups', + 'Team.ReadBasic.All': 'Read Microsoft teams', + 'TeamMember.Read.All': 'Read team members', + 'Mail.ReadWrite': 'Read and write Microsoft emails', + 'Mail.ReadBasic': 'Read Microsoft emails', + 'Mail.Read': 'Read Microsoft emails', + 'Mail.Send': 'Send emails', + 'Calendars.ReadWrite': 'Read and manage Outlook calendar events', + 'Files.Read': 'Read OneDrive files', + 'Files.ReadWrite': 'Read and write OneDrive files', + 'Files.Read.All': 'Read files shared with you, including SharePoint libraries', + 'Files.ReadWrite.All': 'Read and write files you have access to, including SharePoint libraries', + 'Tasks.ReadWrite': 'Read and manage Planner tasks', + 'Sites.Read.All': 'Read SharePoint sites', + 'Sites.ReadWrite.All': 'Read and write SharePoint sites', + 'Sites.Manage.All': 'Manage SharePoint sites', + 'https://dynamics.microsoft.com/user_impersonation': 'Access Microsoft Dataverse on your behalf', + 'User.ReadWrite.All': 'Read and write all user profiles', + 'GroupMember.ReadWrite.All': 'Read and write all group memberships', + 'Directory.Read.All': 'Read directory data', + 'LicenseAssignment.Read.All': 'Read license assignments and subscribed SKUs', + 'LicenseAssignment.ReadWrite.All': 'Read, assign, and remove user licenses', + 'UserAuthenticationMethod.ReadWrite.All': + 'Read and reset authentication methods and passwords for all users', + 'AuditLog.Read.All': 'Read sign-in and directory audit logs', + 'Application.Read.All': 'Read all applications and service principals', + 'AppRoleAssignment.ReadWrite.All': 'Read, grant, and revoke application role assignments', + 'RoleManagement.ReadWrite.Directory': 'Read and manage directory role assignments', + 'Device.Read.All': 'Read all devices', + 'Policy.Read.All': 'Read conditional access and other policies', + + // Reddit scopes + identity: 'Access Reddit identity', + submit: 'Submit posts and comments', + vote: 'Vote on posts and comments', + save: 'Save and unsave posts and comments', + edit: 'Edit posts and comments', + subscribe: 'Subscribe and unsubscribe from subreddits', + history: 'Access Reddit history', + privatemessages: 'Access inbox and send private messages', + account: 'Update account preferences and settings', + mysubreddits: 'Access subscribed and moderated subreddits', + flair: 'Manage user and post flair', + report: 'Report posts and comments for rule violations', + modposts: 'Approve, remove, and moderate posts in moderated subreddits', + modflair: 'Manage flair in moderated subreddits', + modmail: 'Access and respond to moderator mail', + + // Wealthbox scopes + login: 'Access Wealthbox account', + data: 'Access Wealthbox data', + + // Bare read/write, shared by Linear, Trello and Reddit. The generic wording is + // the fallback; each provider's own meaning is in PROVIDER_SCOPE_DESCRIPTIONS. + read: 'Read access to connected account data', + write: 'Write access to connected account data', + + // Slack scopes + 'channels:read': 'View public channels', + 'channels:history': 'Read channel messages', + 'channels:manage': 'Create, archive, and rename public channels', + 'groups:read': 'View private channels', + 'groups:history': 'Read private messages', + 'groups:write': 'Create, archive, and manage private channels', + 'chat:write': 'Send messages', + 'chat:write.public': 'Post to public channels', + 'chat:write.customize': 'Customize message username and icon', + 'assistant:write': 'Manage assistant status, titles, and suggested prompts', + 'im:write': 'Send direct messages', + 'im:history': 'Read direct message history', + 'im:read': 'View direct message channels', + 'users:read': 'View workspace users', + 'users:read.email': 'View user email addresses', + 'files:write': 'Upload files', + 'files:read': 'Download and read files', + 'canvases:read': 'Read canvas sections', + 'canvases:write': 'Create, edit, and delete canvas documents', + 'reactions:write': 'Add emoji reactions to messages', + 'reactions:read': 'View emoji reactions on messages', + + // Webflow scopes + 'sites:read': 'View Webflow sites', + 'sites:write': 'Manage webhooks and site settings', + 'cms:read': 'View CMS content', + 'cms:write': 'Manage CMS content', + 'forms:read': 'View form submissions', + + // HubSpot scopes + 'crm.objects.contacts.read': 'Read HubSpot contacts', + 'crm.objects.contacts.write': 'Create and update HubSpot contacts', + 'crm.objects.companies.read': 'Read HubSpot companies', + 'crm.objects.companies.write': 'Create and update HubSpot companies', + 'crm.objects.deals.read': 'Read HubSpot deals', + 'crm.objects.deals.write': 'Create and update HubSpot deals', + 'crm.objects.owners.read': 'Read HubSpot object owners', + 'crm.objects.users.read': 'Read HubSpot users', + 'crm.objects.users.write': 'Create and update HubSpot users', + 'crm.objects.marketing_events.read': 'Read HubSpot marketing events', + 'crm.objects.marketing_events.write': 'Create and update HubSpot marketing events', + 'crm.objects.line_items.read': 'Read HubSpot line items', + 'crm.objects.line_items.write': 'Create and update HubSpot line items', + 'crm.objects.quotes.read': 'Read HubSpot quotes', + 'crm.objects.quotes.write': 'Create and update HubSpot quotes', + 'crm.objects.appointments.read': 'Read HubSpot appointments', + 'crm.objects.appointments.write': 'Create and update HubSpot appointments', + 'crm.objects.carts.read': 'Read HubSpot shopping carts', + 'crm.objects.carts.write': 'Create and update HubSpot shopping carts', + 'sales-email-read': 'Read the content of HubSpot email engagements', + 'crm.import': 'Import data into HubSpot', + 'crm.lists.read': 'Read HubSpot lists', + 'crm.lists.write': 'Create and update HubSpot lists', + 'crm.objects.tickets.read': 'Read HubSpot tickets', + 'crm.objects.tickets.write': 'Create and update HubSpot tickets', + tickets: 'Access HubSpot tickets', + oauth: 'Authenticate with HubSpot OAuth', + + // Salesforce scopes + api: 'Access Salesforce API', + refresh_token: 'Maintain long-term access to Salesforce account', + + // Asana scopes + default: 'Access Asana workspace', + + // Pipedrive scopes + base: 'Basic access to Pipedrive account', + 'deals:read': 'Read Pipedrive deals', + 'deals:full': 'Full access to manage Pipedrive deals', + 'contacts:read': 'Read Pipedrive contacts', + 'contacts:full': 'Full access to manage Pipedrive contacts', + 'leads:read': 'Read Pipedrive leads', + 'leads:full': 'Full access to manage Pipedrive leads', + 'activities:read': 'Read Pipedrive activities', + 'activities:full': 'Full access to manage Pipedrive activities', + 'mail:read': 'Read Pipedrive emails', + 'mail:full': 'Full access to manage Pipedrive emails', + 'projects:read': 'Read Pipedrive projects', + 'projects:full': 'Full access to manage Pipedrive projects', + 'webhooks:full': 'Full access to manage Pipedrive webhooks', + + // LinkedIn scopes + w_member_social: 'Post, comment, and like posts on your behalf', + + // Instagram scopes (Business Login for Instagram) + instagram_business_basic: 'Access Instagram professional profile and media', + instagram_business_content_publish: 'Publish photos, videos, reels, and stories', + instagram_business_manage_comments: 'Read, reply to, hide, and delete comments', + instagram_business_manage_messages: 'Read conversations and send Instagram Direct messages', + instagram_business_manage_insights: 'Read account and media insights', + + // Box scopes + root_readwrite: 'Read and write all files and folders in Box account', + root_readonly: 'Read all files and folders in Box account', + 'sign_requests.readwrite': 'Create and manage Box Sign e-signature requests', + + // Shopify scopes + write_products: 'Read and manage Shopify products', + write_orders: 'Read and manage Shopify orders', + write_customers: 'Read and manage Shopify customers', + write_inventory: 'Read and manage Shopify inventory levels', + read_locations: 'View store locations', + write_merchant_managed_fulfillment_orders: 'Read orders and create fulfillments for them', + + // Zoom scopes + 'user:read:user': 'View Zoom profile information', + 'meeting:write:meeting': 'Create Zoom meetings', + 'meeting:read:meeting': 'View Zoom meeting details', + 'meeting:read:list_meetings': 'List Zoom meetings', + 'meeting:update:meeting': 'Update Zoom meetings', + 'meeting:delete:meeting': 'Delete Zoom meetings', + 'meeting:read:invitation': 'View Zoom meeting invitations', + 'meeting:read:list_past_participants': 'View past meeting participants', + 'cloud_recording:read:list_user_recordings': 'List Zoom cloud recordings', + 'cloud_recording:read:list_recording_files': 'View recording files', + 'cloud_recording:delete:recording_file': 'Delete cloud recordings', + + // Dropbox scopes + 'account_info.read': 'View Dropbox account information', + 'files.metadata.read': 'View file and folder names, sizes, and dates', + 'files.metadata.write': 'Modify file and folder metadata', + 'files.content.read': 'Download and read Dropbox files', + 'files.content.write': 'Upload, copy, move, and delete files in Dropbox', + 'sharing.read': 'View shared files and folders', + 'sharing.write': 'Share files and folders with others', + + // WordPress.com scopes + global: 'Full access to manage WordPress.com sites, posts, pages, media, and settings', + + // Spotify scopes + 'user-read-private': 'View Spotify account details', + 'user-read-email': 'View email address on Spotify', + 'user-library-read': 'View saved tracks and albums', + 'user-library-modify': 'Save and remove tracks and albums from library', + 'playlist-read-private': 'View private playlists', + 'playlist-read-collaborative': 'View collaborative playlists', + 'playlist-modify-public': 'Create and manage public playlists', + 'playlist-modify-private': 'Create and manage private playlists', + 'user-read-playback-state': 'View current playback state', + 'user-modify-playback-state': 'Control playback on Spotify devices', + 'user-read-currently-playing': 'View currently playing track', + 'user-read-recently-played': 'View recently played tracks', + 'user-top-read': 'View top artists and tracks', + 'user-follow-read': 'View followed artists and users', + 'user-follow-modify': 'Follow and unfollow artists and users', + 'user-read-playback-position': 'View playback position in podcasts', + 'ugc-image-upload': 'Upload images to Spotify playlists', + + // DocuSign scopes + signature: 'Create and send envelopes for e-signature', + extended: 'Extended access to DocuSign account features', + + // Attio scopes + 'record_permission:read-write': 'Read and write CRM records', + 'object_configuration:read-write': 'Read and manage object schemas', + 'list_configuration:read-write': 'Read and manage list configurations', + 'list_entry:read-write': 'Read and write list entries', + 'note:read-write': 'Read and write notes', + 'task:read-write': 'Read and write tasks', + 'comment:read-write': 'Read and write comments and threads', + 'user_management:read': 'View workspace members', + 'webhook:read-write': 'Manage webhooks', + + // Monday.com scopes + 'boards:read': 'Read boards, items, and columns', + 'boards:write': 'Create and modify boards, items, and groups', + 'updates:read': 'Read updates and comments', + 'updates:write': 'Create and edit updates and comments', + 'webhooks:read': 'Read webhook subscriptions', + 'webhooks:write': 'Create and manage webhook subscriptions', + 'me:read': 'Read your user profile', +} + +/** Scope labels that cannot be keyed by scope alone because providers reuse names. */ +const PROVIDER_SCOPE_DESCRIPTIONS: Readonly>>> = { + /** + * Workbooks and Word documents are ordinary drive items, so both integrations + * ask for the generic Files permissions. The shared labels name OneDrive + * specifically, which reads as the wrong product on either consent screen and + * omits the SharePoint libraries the same scopes cover. + */ + 'microsoft-excel': { + 'Files.Read': 'Read your workbooks in OneDrive', + 'Files.ReadWrite': 'Read, create, and edit your workbooks in OneDrive', + 'Files.Read.All': 'Read workbooks shared with you, including SharePoint libraries', + 'Files.ReadWrite.All': + 'Read, create, and edit workbooks you have access to, including SharePoint libraries', + 'Sites.Read.All': 'List the SharePoint sites and document libraries you can open', + }, + 'microsoft-word': { + 'Files.Read': 'Read your Word documents in OneDrive', + 'Files.ReadWrite': 'Read, create, and edit your Word documents in OneDrive', + 'Files.Read.All': 'Read Word documents shared with you, including SharePoint libraries', + 'Files.ReadWrite.All': + 'Read, create, and edit Word documents you have access to, including SharePoint libraries', + }, + /** + * `read` and `write` are not Linear's alone: Trello and Reddit issue scopes + * under the same two names, and the shared labels describe none of them + * precisely. A published table saying "connected account data" for a Trello + * board grant tells the reader nothing about what they are approving. + */ + trello: { + read: 'View boards, lists, and cards you can access', + write: 'Create and modify boards, lists, and cards', + }, + reddit: { + read: 'View posts, comments, and subreddits through your account', + }, + bitbucket: { + account: 'View your Bitbucket account and workspace memberships', + repository: 'View repositories and source code', + 'repository:write': 'Create and modify repositories, branches, and source code', + pullrequest: 'View pull requests, comments, approvals, and statuses', + 'pullrequest:write': 'Create, update, approve, decline, and merge pull requests', + pipeline: 'View pipelines, steps, and logs', + 'pipeline:write': 'Run and stop pipelines', + webhook: 'Manage repository webhooks', + }, +} + +/** + * Get a human-readable description for a scope. + * Falls back to the raw scope string if no description is found. + */ +export function getScopeDescription(scope: string, providerId?: string): string { + return ( + PROVIDER_SCOPE_DESCRIPTIONS[providerId ?? '']?.[scope] || SCOPE_DESCRIPTIONS[scope] || scope + ) +} diff --git a/apps/sim/lib/oauth/utils.test.ts b/apps/sim/lib/oauth/utils.test.ts index ab0e34a9273..50c2e631297 100644 --- a/apps/sim/lib/oauth/utils.test.ts +++ b/apps/sim/lib/oauth/utils.test.ts @@ -8,7 +8,6 @@ import { getCanonicalScopesForProvider, getMissingRequiredScopes, getProviderIdFromServiceId, - getScopeDescription, getScopesForService, getServiceByProviderAndId, getServiceConfigByProviderId, @@ -385,43 +384,6 @@ describe('getCanonicalScopesForProvider', () => { }) }) -describe('getScopeDescription', () => { - it.concurrent('uses provider-specific labels for Bitbucket scope names', () => { - expect(getScopeDescription('account', 'bitbucket')).toBe( - 'View your Bitbucket account and workspace memberships' - ) - expect(getScopeDescription('pipeline:write', 'bitbucket')).toBe('Run and stop pipelines') - expect(getScopeDescription('webhook', 'bitbucket')).toBe('Manage repository webhooks') - }) - - it.concurrent('preserves the existing Reddit meaning of the account scope', () => { - expect(getScopeDescription('account', 'reddit')).toBe('Update account preferences and settings') - expect(getScopeDescription('account')).toBe('Update account preferences and settings') - }) - - /** - * The consent screen is where a user decides what to grant, so a write scope - * has to read as one. `w_member_social` previously said 'Access LinkedIn - * profile', describing a posting grant as a profile read. - * - * The wording tracks LinkedIn's own: "Post, comment, and like posts on behalf - * of an authenticated member." It names all three verbs even though Sim only - * posts -- the label describes the grant the token carries, not Sim's current - * use of it, and LinkedIn's scopes cannot be sub-selected. - */ - it.concurrent('describes w_member_social as the write grant it is', () => { - const description = getScopeDescription('w_member_social', 'linkedin') - - expect(description).toBe('Post, comment, and like posts on your behalf') - expect(description).not.toMatch(/access .*profile/i) - }) - - it.concurrent('leaves the read-only LinkedIn scopes read-only', () => { - expect(getScopeDescription('profile', 'linkedin')).toBe('Access profile information') - expect(getScopeDescription('email', 'linkedin')).toBe('Access email address') - }) -}) - describe('parseProvider', () => { it.concurrent('should parse simple provider without hyphen', () => { const config = parseProvider('slack' as OAuthProvider) diff --git a/apps/sim/lib/oauth/utils.ts b/apps/sim/lib/oauth/utils.ts index 78068eac613..7faca405527 100644 --- a/apps/sim/lib/oauth/utils.ts +++ b/apps/sim/lib/oauth/utils.ts @@ -7,535 +7,6 @@ import type { ProviderConfig, } from './types' -/** - * Centralized human-readable descriptions for OAuth scopes. - * Used by the OAuth Required Modal and available for any UI that needs to display scope info. - */ -export const SCOPE_DESCRIPTIONS: Record = { - // Zoho Desk scopes - 'Desk.tickets.READ': 'View tickets, threads, comments, and attachments', - 'Desk.tickets.UPDATE': 'Update tickets and add comments', - 'Desk.contacts.READ': 'View contacts', - 'Desk.agents.READ': 'View agents', - 'Desk.basic.READ': 'View basic account and organization data', - 'Desk.webhooks.CREATE': 'Create webhooks', - 'Desk.webhooks.DELETE': 'Delete webhooks', - 'aaaserver.profile.READ': 'View your Zoho profile', - // ManageEngine ServiceDesk Plus Cloud scopes - 'SDPOnDemand.requests.CREATE': 'Create requests', - 'SDPOnDemand.requests.READ': 'View requests and their notes', - 'SDPOnDemand.requests.UPDATE': 'Update requests and add notes', - 'SDPOnDemand.requests.DELETE': 'Delete requests', - 'SDPOnDemand.problems.CREATE': 'Create problems', - 'SDPOnDemand.problems.READ': 'View problems and their notes', - 'SDPOnDemand.problems.UPDATE': 'Update problems and add notes', - 'SDPOnDemand.problems.DELETE': 'Delete problems', - 'SDPOnDemand.changes.CREATE': 'Create changes', - 'SDPOnDemand.changes.READ': 'View changes and their notes', - 'SDPOnDemand.changes.UPDATE': 'Update changes and add notes', - 'SDPOnDemand.changes.DELETE': 'Delete changes', - 'SDPOnDemand.assets.CREATE': 'Create assets', - 'SDPOnDemand.assets.READ': 'View assets', - 'SDPOnDemand.assets.UPDATE': 'Update assets', - 'SDPOnDemand.assets.DELETE': 'Delete assets', - 'SDPOnDemand.solutions.CREATE': 'Create knowledge base solutions', - 'SDPOnDemand.solutions.READ': 'View knowledge base solutions', - 'SDPOnDemand.solutions.UPDATE': 'Update knowledge base solutions', - 'SDPOnDemand.solutions.DELETE': 'Delete knowledge base solutions', - // Google scopes - 'https://www.googleapis.com/auth/gmail.send': 'Send emails', - 'https://www.googleapis.com/auth/gmail.labels': 'View and manage email labels', - 'https://www.googleapis.com/auth/gmail.readonly': 'View email messages and settings', - 'https://www.googleapis.com/auth/gmail.modify': 'View and manage email messages', - 'https://www.googleapis.com/auth/drive.file': 'View and manage Google Drive files', - 'https://www.googleapis.com/auth/drive': 'Access all Google Drive files', - 'https://www.googleapis.com/auth/calendar.readonly': 'View calendars and events', - 'https://www.googleapis.com/auth/calendar': 'View and manage calendar', - 'https://www.googleapis.com/auth/contacts': 'View and manage Google Contacts', - 'https://www.googleapis.com/auth/tasks': 'Create, read, update, and delete Google Tasks', - 'https://www.googleapis.com/auth/userinfo.email': 'View email address', - 'https://www.googleapis.com/auth/userinfo.profile': 'View basic profile info', - 'https://www.googleapis.com/auth/forms.body': 'View and manage Google Forms', - 'https://www.googleapis.com/auth/forms.responses.readonly': 'View responses to Google Forms', - 'https://www.googleapis.com/auth/adwords': 'Manage Google Ads campaigns and reporting', - 'https://www.googleapis.com/auth/bigquery': 'View and manage data in Google BigQuery', - 'https://www.googleapis.com/auth/ediscovery': 'Access Google Vault for eDiscovery', - 'https://www.googleapis.com/auth/ediscovery.readonly': - 'View Google Vault matters, holds, and saved queries', - 'https://www.googleapis.com/auth/devstorage.read_only': 'Read files from Google Cloud Storage', - 'https://www.googleapis.com/auth/admin.directory.group': 'Manage Google Workspace groups', - 'https://www.googleapis.com/auth/admin.directory.group.member': - 'Manage Google Workspace group memberships', - 'https://www.googleapis.com/auth/admin.directory.group.readonly': 'View Google Workspace groups', - 'https://www.googleapis.com/auth/admin.directory.group.member.readonly': - 'View Google Workspace group memberships', - 'https://www.googleapis.com/auth/chat.spaces.readonly': - 'View Google Chat spaces you are a member of', - 'https://www.googleapis.com/auth/chat.messages.readonly': - 'View messages in Google Chat spaces you are a member of', - 'https://www.googleapis.com/auth/meetings.space.created': - 'Create and manage Google Meet meeting spaces', - 'https://www.googleapis.com/auth/meetings.space.readonly': - 'View Google Meet meeting space details', - 'https://www.googleapis.com/auth/cloud-platform': - 'Full access to Google Cloud resources for Vertex AI', - - // Confluence scopes - 'read:confluence-content.all': 'Read all Confluence content', - 'read:confluence-space.summary': 'Read Confluence space information', - 'read:space:confluence': 'View Confluence spaces', - 'read:space-details:confluence': 'View detailed Confluence space information', - 'write:confluence-content': 'Create and edit Confluence pages', - 'write:confluence-space': 'Manage Confluence spaces', - 'write:confluence-file': 'Upload files to Confluence', - 'read:content:confluence': 'Read Confluence content', - 'read:page:confluence': 'View Confluence pages', - 'write:page:confluence': 'Create and update Confluence pages', - 'read:comment:confluence': 'View comments on Confluence pages', - 'write:comment:confluence': 'Create and update comments', - 'delete:comment:confluence': 'Delete comments from Confluence pages', - 'read:attachment:confluence': 'View attachments on Confluence pages', - 'write:attachment:confluence': 'Upload and manage attachments', - 'delete:attachment:confluence': 'Delete attachments from Confluence pages', - 'delete:page:confluence': 'Delete Confluence pages', - 'read:label:confluence': 'View labels on Confluence content', - 'write:label:confluence': 'Add and remove labels', - 'search:confluence': 'Search Confluence content', - 'readonly:content.attachment:confluence': 'View attachments', - 'read:blogpost:confluence': 'View Confluence blog posts', - 'write:blogpost:confluence': 'Create and update Confluence blog posts', - 'read:content.property:confluence': 'View properties on Confluence content', - 'write:content.property:confluence': 'Create and manage content properties', - 'read:hierarchical-content:confluence': 'View page hierarchy (children and ancestors)', - 'read:content.metadata:confluence': 'View content metadata (required for ancestors)', - 'read:user:confluence': 'View Confluence user profiles', - 'read:confluence-user': 'View Confluence user profiles (v1 API)', - 'read:task:confluence': 'View Confluence inline tasks', - 'write:task:confluence': 'Update Confluence inline tasks', - 'delete:blogpost:confluence': 'Delete Confluence blog posts', - 'write:space:confluence': 'Create and update Confluence spaces', - 'delete:space:confluence': 'Delete Confluence spaces', - 'read:space.property:confluence': 'View Confluence space properties', - 'write:space.property:confluence': 'Create and manage space properties', - 'read:space.permission:confluence': 'View Confluence space permissions', - - // Common scopes - 'read:me': 'Read profile information', - offline_access: 'Access account when not using the application', - openid: 'Standard authentication', - profile: 'Access profile information', - email: 'Access email address', - - // Notion scopes - 'database.read': 'Read database', - 'database.write': 'Write to database', - 'projects.read': 'Read projects', - 'page.read': 'Read Notion pages', - 'page.write': 'Write to Notion pages', - 'workspace.content': 'Read Notion content', - 'workspace.name': 'Read Notion workspace name', - 'workspace.read': 'Read Notion workspace', - 'workspace.write': 'Write to Notion workspace', - 'user.email:read': 'Read email address', - - // GitHub scopes - repo: 'Access repositories', - workflow: 'Manage repository workflows', - 'read:user': 'Read public user information', - 'user:email': 'Access email address', - - // X (Twitter) scopes - 'tweet.read': 'Read tweets and timeline', - 'tweet.write': 'Post and delete tweets', - 'tweet.moderate.write': 'Hide and unhide replies to tweets', - 'users.read': 'Read user profiles and account information', - 'follows.read': 'View followers and following lists', - 'follows.write': 'Follow and unfollow users', - 'bookmark.read': 'View bookmarked tweets', - 'bookmark.write': 'Add and remove bookmarks', - 'like.read': 'View liked tweets and liking users', - 'like.write': 'Like and unlike tweets', - 'block.read': 'View blocked users', - 'block.write': 'Block and unblock users', - 'mute.read': 'View muted users', - 'mute.write': 'Mute and unmute users', - 'offline.access': 'Access account when not using the application', - - // TikTok scopes - 'user.info.basic': "Read a user's profile info (open id, avatar, display name)", - 'user.info.profile': "Read a user's profile info (bio, verification status, username)", - 'user.info.stats': "Read a user's stats (follower, following, likes, and video counts)", - 'video.upload': "Share content to a creator's account as a draft for further edit and post", - 'video.list': "Read a user's public TikTok videos", - - // Airtable scopes - 'data.records:read': 'Read records', - 'data.records:write': 'Write to records', - 'schema.bases:read': 'View bases and tables', - 'webhook:manage': 'Manage webhooks', - - // Jira scopes - 'read:jira-user': 'Read Jira user', - 'read:jira-work': 'Read Jira work', - 'write:jira-work': 'Write to Jira work', - 'manage:jira-webhook': 'Register and manage Jira webhooks', - 'read:webhook:jira': 'View Jira webhooks', - 'write:webhook:jira': 'Create and update Jira webhooks', - 'delete:webhook:jira': 'Delete Jira webhooks', - 'read:issue-event:jira': 'Read Jira issue events', - 'write:issue:jira': 'Write to Jira issues', - 'read:project:jira': 'Read Jira projects', - 'read:issue-type:jira': 'Read Jira issue types', - 'read:issue-meta:jira': 'Read Jira issue meta', - 'read:issue-security-level:jira': 'Read Jira issue security level', - 'read:issue.vote:jira': 'Read Jira issue votes', - 'read:issue.changelog:jira': 'Read Jira issue changelog', - 'read:avatar:jira': 'Read Jira avatar', - 'read:issue:jira': 'Read Jira issues', - 'read:status:jira': 'Read Jira status', - 'read:user:jira': 'Read Jira user', - 'read:field-configuration:jira': 'Read Jira field configuration', - 'read:issue-details:jira': 'Read Jira issue details', - 'read:field:jira': 'Read Jira field configurations', - 'read:jql:jira': 'Use JQL to filter Jira issues', - 'read:comment.property:jira': 'Read Jira comment properties', - 'read:issue.property:jira': 'Read Jira issue properties', - 'delete:issue:jira': 'Delete Jira issues', - 'write:comment:jira': 'Add and update comments on Jira issues', - 'read:comment:jira': 'Read comments on Jira issues', - 'delete:comment:jira': 'Delete comments from Jira issues', - 'read:attachment:jira': 'Read attachments from Jira issues', - 'write:attachment:jira': 'Add attachments to Jira issues', - 'delete:attachment:jira': 'Delete attachments from Jira issues', - 'write:issue-worklog:jira': 'Add and update worklog entries on Jira issues', - 'read:issue-worklog:jira': 'Read worklog entries from Jira issues', - 'delete:issue-worklog:jira': 'Delete worklog entries from Jira issues', - 'write:issue-link:jira': 'Create links between Jira issues', - 'delete:issue-link:jira': 'Delete links between Jira issues', - - // Jira Service Management scopes - 'read:servicedesk-request': 'View service desk requests', - 'write:servicedesk-request': 'Create and update service desk requests', - 'manage:servicedesk-customer': 'Manage service desk customers and organizations', - 'read:servicedesk:jira-service-management': 'View service desks and their settings', - 'read:requesttype:jira-service-management': 'View request types available in service desks', - 'read:request:jira-service-management': 'View customer requests in service desks', - 'write:request:jira-service-management': 'Create customer requests in service desks', - 'read:request.comment:jira-service-management': 'View comments on customer requests', - 'write:request.comment:jira-service-management': 'Add comments to customer requests', - 'read:customer:jira-service-management': 'View customer information', - 'write:customer:jira-service-management': 'Create and manage customers', - 'read:servicedesk.customer:jira-service-management': 'View customers linked to service desks', - 'write:servicedesk.customer:jira-service-management': - 'Add and remove customers from service desks', - 'read:organization:jira-service-management': 'View organizations', - 'write:organization:jira-service-management': 'Create and manage organizations', - 'read:servicedesk.organization:jira-service-management': - 'View organizations linked to service desks', - 'write:servicedesk.organization:jira-service-management': - 'Add and remove organizations from service desks', - 'read:organization.user:jira-service-management': 'View users in organizations', - 'write:organization.user:jira-service-management': 'Add and remove users from organizations', - 'read:organization.property:jira-service-management': 'View organization properties', - 'write:organization.property:jira-service-management': - 'Create and manage organization properties', - 'read:organization.profile:jira-service-management': 'View organization profiles', - 'write:organization.profile:jira-service-management': 'Update organization profiles', - 'read:queue:jira-service-management': 'View service desk queues and their issues', - 'read:request.sla:jira-service-management': 'View SLA information for customer requests', - 'read:request.status:jira-service-management': 'View status of customer requests', - 'write:request.status:jira-service-management': 'Transition customer request status', - 'read:request.participant:jira-service-management': 'View participants on customer requests', - 'write:request.participant:jira-service-management': - 'Add and remove participants from customer requests', - 'read:request.approval:jira-service-management': 'View approvals on customer requests', - 'write:request.approval:jira-service-management': 'Approve or decline customer requests', - 'read:cmdb-object:jira': 'View Assets objects and run AQL searches', - 'write:cmdb-object:jira': 'Create and update Assets objects', - 'delete:cmdb-object:jira': 'Delete Assets objects', - 'read:cmdb-schema:jira': 'View Assets object schemas', - 'read:cmdb-type:jira': 'View Assets object types', - 'read:cmdb-attribute:jira': 'View Assets object type attributes', - - // Microsoft scopes - 'User.Read': 'Read Microsoft user', - 'Chat.Read': 'Read Microsoft chats', - 'Chat.ReadWrite': 'Write to Microsoft chats', - 'Chat.ReadBasic': 'Read Microsoft chats', - 'ChatMessage.Send': 'Send chat messages', - 'Channel.ReadBasic.All': 'Read Microsoft channels', - 'ChannelMessage.Send': 'Write to Microsoft channels', - 'ChannelMessage.Read.All': 'Read Microsoft channels', - 'ChannelMessage.ReadWrite': 'Read and write to Microsoft channels', - 'ChannelMember.Read.All': 'Read team channel members', - 'Group.Read.All': 'Read Microsoft groups', - 'Group.ReadWrite.All': 'Read and write all groups', - 'Team.ReadBasic.All': 'Read Microsoft teams', - 'TeamMember.Read.All': 'Read team members', - 'Mail.ReadWrite': 'Write to Microsoft emails', - 'Mail.ReadBasic': 'Read Microsoft emails', - 'Mail.Read': 'Read Microsoft emails', - 'Mail.Send': 'Send emails', - 'Calendars.ReadWrite': 'Read and manage Outlook calendar events', - 'Files.Read': 'Read OneDrive files', - 'Files.ReadWrite': 'Read and write OneDrive files', - 'Files.Read.All': 'Read files shared with you, including SharePoint libraries', - 'Files.ReadWrite.All': 'Read and write files you have access to, including SharePoint libraries', - 'Tasks.ReadWrite': 'Read and manage Planner tasks', - 'Sites.Read.All': 'Read Sharepoint sites', - 'Sites.ReadWrite.All': 'Read and write Sharepoint sites', - 'Sites.Manage.All': 'Manage Sharepoint sites', - 'https://dynamics.microsoft.com/user_impersonation': 'Access Microsoft Dataverse on your behalf', - 'User.ReadWrite.All': 'Read and write all user profiles', - 'GroupMember.ReadWrite.All': 'Read and write all group memberships', - 'Directory.Read.All': 'Read directory data', - 'LicenseAssignment.Read.All': 'Read license assignments and subscribed SKUs', - 'LicenseAssignment.ReadWrite.All': 'Assign and remove user licenses', - 'UserAuthenticationMethod.ReadWrite.All': - 'Read and reset authentication methods and passwords for all users', - 'AuditLog.Read.All': 'Read sign-in and directory audit logs', - 'Application.Read.All': 'Read all applications and service principals', - 'AppRoleAssignment.ReadWrite.All': 'Grant and revoke application role assignments', - 'RoleManagement.ReadWrite.Directory': 'Read and manage directory role assignments', - 'Device.Read.All': 'Read all devices', - 'Policy.Read.All': 'Read conditional access and other policies', - - // Reddit scopes - identity: 'Access Reddit identity', - submit: 'Submit posts and comments', - vote: 'Vote on posts and comments', - save: 'Save and unsave posts and comments', - edit: 'Edit posts and comments', - subscribe: 'Subscribe and unsubscribe from subreddits', - history: 'Access Reddit history', - privatemessages: 'Access inbox and send private messages', - account: 'Update account preferences and settings', - mysubreddits: 'Access subscribed and moderated subreddits', - flair: 'Manage user and post flair', - report: 'Report posts and comments for rule violations', - modposts: 'Approve, remove, and moderate posts in moderated subreddits', - modflair: 'Manage flair in moderated subreddits', - modmail: 'Access and respond to moderator mail', - - // Wealthbox scopes - login: 'Access Wealthbox account', - data: 'Access Wealthbox data', - - // Linear scopes - read: 'Read access to connected account data', - write: 'Write access to connected account data', - - // Slack scopes - 'channels:read': 'View public channels', - 'channels:history': 'Read channel messages', - 'channels:manage': 'Create, archive, and rename public channels', - 'groups:read': 'View private channels', - 'groups:history': 'Read private messages', - 'groups:write': 'Create, archive, and manage private channels', - 'chat:write': 'Send messages', - 'chat:write.public': 'Post to public channels', - 'chat:write.customize': 'Customize message username and icon', - 'assistant:write': 'Manage assistant status, titles, and suggested prompts', - 'im:write': 'Send direct messages', - 'im:history': 'Read direct message history', - 'im:read': 'View direct message channels', - 'users:read': 'View workspace users', - 'users:read.email': 'View user email addresses', - 'files:write': 'Upload files', - 'files:read': 'Download and read files', - 'canvases:read': 'Read canvas sections', - 'canvases:write': 'Create, edit, and delete canvas documents', - 'reactions:write': 'Add emoji reactions to messages', - 'reactions:read': 'View emoji reactions on messages', - - // Webflow scopes - 'sites:read': 'View Webflow sites', - 'sites:write': 'Manage webhooks and site settings', - 'cms:read': 'View CMS content', - 'cms:write': 'Manage CMS content', - 'forms:read': 'View form submissions', - - // HubSpot scopes - 'crm.objects.contacts.read': 'Read HubSpot contacts', - 'crm.objects.contacts.write': 'Create and update HubSpot contacts', - 'crm.objects.companies.read': 'Read HubSpot companies', - 'crm.objects.companies.write': 'Create and update HubSpot companies', - 'crm.objects.deals.read': 'Read HubSpot deals', - 'crm.objects.deals.write': 'Create and update HubSpot deals', - 'crm.objects.owners.read': 'Read HubSpot object owners', - 'crm.objects.users.read': 'Read HubSpot users', - 'crm.objects.users.write': 'Create and update HubSpot users', - 'crm.objects.marketing_events.read': 'Read HubSpot marketing events', - 'crm.objects.marketing_events.write': 'Create and update HubSpot marketing events', - 'crm.objects.line_items.read': 'Read HubSpot line items', - 'crm.objects.line_items.write': 'Create and update HubSpot line items', - 'crm.objects.quotes.read': 'Read HubSpot quotes', - 'crm.objects.quotes.write': 'Create and update HubSpot quotes', - 'crm.objects.appointments.read': 'Read HubSpot appointments', - 'crm.objects.appointments.write': 'Create and update HubSpot appointments', - 'crm.objects.carts.read': 'Read HubSpot shopping carts', - 'crm.objects.carts.write': 'Create and update HubSpot shopping carts', - 'sales-email-read': 'Read the content of HubSpot email engagements', - 'crm.import': 'Import data into HubSpot', - 'crm.lists.read': 'Read HubSpot lists', - 'crm.lists.write': 'Create and update HubSpot lists', - 'crm.objects.tickets.read': 'Read HubSpot tickets', - 'crm.objects.tickets.write': 'Create and update HubSpot tickets', - tickets: 'Access HubSpot tickets', - oauth: 'Authenticate with HubSpot OAuth', - - // Salesforce scopes - api: 'Access Salesforce API', - refresh_token: 'Maintain long-term access to Salesforce account', - - // Asana scopes - default: 'Access Asana workspace', - - // Pipedrive scopes - base: 'Basic access to Pipedrive account', - 'deals:read': 'Read Pipedrive deals', - 'deals:full': 'Full access to manage Pipedrive deals', - 'contacts:read': 'Read Pipedrive contacts', - 'contacts:full': 'Full access to manage Pipedrive contacts', - 'leads:read': 'Read Pipedrive leads', - 'leads:full': 'Full access to manage Pipedrive leads', - 'activities:read': 'Read Pipedrive activities', - 'activities:full': 'Full access to manage Pipedrive activities', - 'mail:read': 'Read Pipedrive emails', - 'mail:full': 'Full access to manage Pipedrive emails', - 'projects:read': 'Read Pipedrive projects', - 'projects:full': 'Full access to manage Pipedrive projects', - 'webhooks:full': 'Full access to manage Pipedrive webhooks', - - // LinkedIn scopes - w_member_social: 'Post, comment, and like posts on your behalf', - - // Instagram scopes (Business Login for Instagram) - instagram_business_basic: 'Access Instagram professional profile and media', - instagram_business_content_publish: 'Publish photos, videos, reels, and stories', - instagram_business_manage_comments: 'Read, reply to, hide, and delete comments', - instagram_business_manage_messages: 'Read conversations and send Instagram Direct messages', - instagram_business_manage_insights: 'Read account and media insights', - - // Box scopes - root_readwrite: 'Read and write all files and folders in Box account', - root_readonly: 'Read all files and folders in Box account', - 'sign_requests.readwrite': 'Create and manage Box Sign e-signature requests', - - // Shopify scopes - write_products: 'Read and manage Shopify products', - write_orders: 'Read and manage Shopify orders', - write_customers: 'Read and manage Shopify customers', - write_inventory: 'Read and manage Shopify inventory levels', - read_locations: 'View store locations', - write_merchant_managed_fulfillment_orders: 'Create fulfillments for orders', - - // Zoom scopes - 'user:read:user': 'View Zoom profile information', - 'meeting:write:meeting': 'Create Zoom meetings', - 'meeting:read:meeting': 'View Zoom meeting details', - 'meeting:read:list_meetings': 'List Zoom meetings', - 'meeting:update:meeting': 'Update Zoom meetings', - 'meeting:delete:meeting': 'Delete Zoom meetings', - 'meeting:read:invitation': 'View Zoom meeting invitations', - 'meeting:read:list_past_participants': 'View past meeting participants', - 'cloud_recording:read:list_user_recordings': 'List Zoom cloud recordings', - 'cloud_recording:read:list_recording_files': 'View recording files', - 'cloud_recording:delete:recording_file': 'Delete cloud recordings', - - // Dropbox scopes - 'account_info.read': 'View Dropbox account information', - 'files.metadata.read': 'View file and folder names, sizes, and dates', - 'files.metadata.write': 'Modify file and folder metadata', - 'files.content.read': 'Download and read Dropbox files', - 'files.content.write': 'Upload, copy, move, and delete files in Dropbox', - 'sharing.read': 'View shared files and folders', - 'sharing.write': 'Share files and folders with others', - - // WordPress.com scopes - global: 'Full access to manage WordPress.com sites, posts, pages, media, and settings', - - // Spotify scopes - 'user-read-private': 'View Spotify account details', - 'user-read-email': 'View email address on Spotify', - 'user-library-read': 'View saved tracks and albums', - 'user-library-modify': 'Save and remove tracks and albums from library', - 'playlist-read-private': 'View private playlists', - 'playlist-read-collaborative': 'View collaborative playlists', - 'playlist-modify-public': 'Create and manage public playlists', - 'playlist-modify-private': 'Create and manage private playlists', - 'user-read-playback-state': 'View current playback state', - 'user-modify-playback-state': 'Control playback on Spotify devices', - 'user-read-currently-playing': 'View currently playing track', - 'user-read-recently-played': 'View recently played tracks', - 'user-top-read': 'View top artists and tracks', - 'user-follow-read': 'View followed artists and users', - 'user-follow-modify': 'Follow and unfollow artists and users', - 'user-read-playback-position': 'View playback position in podcasts', - 'ugc-image-upload': 'Upload images to Spotify playlists', - - // DocuSign scopes - signature: 'Create and send envelopes for e-signature', - extended: 'Extended access to DocuSign account features', - - // Attio scopes - 'record_permission:read-write': 'Read and write CRM records', - 'object_configuration:read-write': 'Read and manage object schemas', - 'list_configuration:read-write': 'Read and manage list configurations', - 'list_entry:read-write': 'Read and write list entries', - 'note:read-write': 'Read and write notes', - 'task:read-write': 'Read and write tasks', - 'comment:read-write': 'Read and write comments and threads', - 'user_management:read': 'View workspace members', - 'webhook:read-write': 'Manage webhooks', - - // Monday.com scopes - 'boards:read': 'Read boards, items, and columns', - 'boards:write': 'Create and modify boards, items, and groups', - 'updates:read': 'Read updates and comments', - 'updates:write': 'Create and edit updates and comments', - 'webhooks:read': 'Read webhook subscriptions', - 'webhooks:write': 'Create and manage webhook subscriptions', - 'me:read': 'Read your user profile', -} - -/** Scope labels that cannot be keyed by scope alone because providers reuse names. */ -const PROVIDER_SCOPE_DESCRIPTIONS: Readonly>>> = { - /** - * Word documents are ordinary drive items, so the integration asks for the - * generic Files permissions. The shared labels name OneDrive specifically, - * which reads as the wrong product on the Word consent screen and omits the - * SharePoint libraries the same scopes cover. - */ - 'microsoft-word': { - 'Files.Read': 'Read your Word documents in OneDrive', - 'Files.ReadWrite': 'Read, create, and edit your Word documents in OneDrive', - 'Files.Read.All': 'Read Word documents shared with you, including SharePoint libraries', - 'Files.ReadWrite.All': - 'Read, create, and edit Word documents you have access to, including SharePoint libraries', - }, - bitbucket: { - account: 'View your Bitbucket account and workspace memberships', - repository: 'View repositories and source code', - 'repository:write': 'Create and modify repositories, branches, and source code', - pullrequest: 'View pull requests, comments, approvals, and statuses', - 'pullrequest:write': 'Create, update, approve, decline, and merge pull requests', - pipeline: 'View pipelines, steps, and logs', - 'pipeline:write': 'Run and stop pipelines', - webhook: 'Manage repository webhooks', - }, -} - -/** - * Get a human-readable description for a scope. - * Falls back to the raw scope string if no description is found. - */ -export function getScopeDescription(scope: string, providerId?: string): string { - return ( - PROVIDER_SCOPE_DESCRIPTIONS[providerId ?? '']?.[scope] || SCOPE_DESCRIPTIONS[scope] || scope - ) -} - /** * Returns a flat list of all available OAuth services with metadata. * This is safe to use on the server as it doesn't include React components. diff --git a/scripts/generate-docs.test.ts b/scripts/generate-docs.test.ts index 2f6448145d6..46f0f3a7444 100644 --- a/scripts/generate-docs.test.ts +++ b/scripts/generate-docs.test.ts @@ -1,14 +1,20 @@ import fs from 'fs' import path from 'path' +import { OAUTH_CLIENT_CAPABILITIES } from '@sim/deployment-config/env-capabilities' import { describe, expect, it } from 'vitest' +import { OAUTH_SCOPES } from '../apps/sim/lib/oauth/scopes' import { + buildScopesSection, extractAllBlockConfigs, extractBlockSuppliedParamIds, extractToolInfo, extractUserSettableParamIds, getToolInfo, + loadClientIdEnvByProviderId, + loadOAuthConnectCatalog, parseConstProperties, parsePropertiesContent, + sharedScopesAcrossConnectors, } from './generate-docs' describe('documentation tool metadata', () => { @@ -943,3 +949,198 @@ describe('template interpolation is lexed rather than brace-counted', () => { expect(ids).toEqual(['a', 'b']) }) }) + +describe('the generated Scopes section', () => { + /** + * A service account or a pasted API token connects the same integration + * without this flow, and neither is governed by the table, so the sentence + * names the flow rather than the integration. + */ + it('attributes the table to the OAuth flow rather than to the integration', () => { + expect(buildScopesSection('sharepoint', 'SharePoint')).toContain( + 'Connecting SharePoint through OAuth requests these scopes.' + ) + }) + + it('lists every scope the service requests, with a label for each', () => { + const section = buildScopesSection('sharepoint', 'SharePoint') + + for (const scope of OAUTH_SCOPES.sharepoint) { + expect(section).toContain(`| \`${scope}\` |`) + } + expect(section).not.toMatch(/\| {2}\|/) + }) + + /** + * Slack's approval-gated scopes are appended from an environment flag, so + * including them would make the published page depend on which deployment + * ran the generator and break `docs:check` for everyone else. + */ + it('keeps scopes that are only requested under an environment flag out of the table', () => { + const section = buildScopesSection('slack', 'Slack') + + expect(section).toContain('| `chat:write` |') + expect(section).not.toMatch(/\| `assistant:write` \|/) + }) + + /** + * The gated scopes stay out of the table so generated output cannot vary by + * deployment, but a self-hoster who sets the flag still has to add them to the + * app: Slack rejects the whole authorization when the app is not approved for + * one, so omitting them entirely costs that reader the integration. + */ + /** + * Dataverse declares a resource scope, but binding an environment swaps it for + * that environment's `/.default`, so the rows alone claim a request the + * runtime does not make. + */ + it('carries a service caveat the rows cannot state on their own', () => { + const section = buildScopesSection('microsoft-dataverse', 'Microsoft Dataverse') + + // The host is normalized to its `.api` form before the request, so the + // example has to be the audience Sim actually asks for. + expect(section).toContain('`https://contoso.api.crm.dynamics.com/.default`') + expect(buildScopesSection('sharepoint', 'SharePoint')).not.toContain('.default') + }) + + /** + * A managed connector unions the declared scopes with its own + * `additionalScopes`, so a Google table printed from the declared set alone + * understates the request by exactly `openid`. Keyed by provider id, which is + * what the connector factory matches on and differs from the service id for + * Gmail. + */ + it('names the scopes a managed connection adds to the declared set', () => { + const gmail = buildScopesSection('gmail', 'Gmail') + + expect(gmail).toContain('A managed connection also requests `openid`') + expect(gmail).not.toMatch(/\| `openid` \|/) + // Vault has no managed connector, so nothing is added to its request. + expect(buildScopesSection('google-vault', 'Google Vault')).not.toContain('managed connection') + }) + + it('names the opt-in scopes in prose even though the table omits them', () => { + const section = buildScopesSection('slack', 'Slack') + + expect(section).toContain('SLACK_EXTENDED_SCOPES') + expect(section).toContain('`assistant:write`') + expect(section).not.toMatch(/\| `assistant:write` \|/) + expect(buildScopesSection('sharepoint', 'SharePoint')).not.toContain('SLACK_EXTENDED_SCOPES') + }) + + /** + * Only some self-issued credentials have permissions to pick. A Wealthbox or + * Monday token carries its creating user's access with no scope picker at all, + * so an instruction to "grant the same scopes" sends the reader hunting for a + * setting that does not exist. + */ + /** + * A provider that hands out one all-or-nothing token has nothing to choose, + * so the heading would introduce an empty table. + */ + it('emits nothing for a service that declares no scopes', () => { + expect(OAUTH_SCOPES.notion).toEqual([]) + expect(buildScopesSection('notion', 'Notion')).toBe('') + }) + + it('refuses to document a block pointing at an unknown OAuth service', () => { + expect(() => buildScopesSection('sharepoint-classic', 'SharePoint')).toThrow( + /no OAUTH_PROVIDERS service declares/ + ) + }) + + it('reads the scope labels under the service own provider id', () => { + expect(loadOAuthConnectCatalog().services.get('gmail')?.providerId).toBe('google-email') + expect(buildScopesSection('microsoft-word', 'Microsoft Word')).toContain( + 'Read your Word documents in OneDrive' + ) + }) +}) + +describe('the self-hosting OAuth app reference', () => { + /** + * The page is what a self-hoster reads before registering an app, so a + * connector missing from its provider's table is a connector nobody can + * connect. The hand-maintained version of this table had lost Microsoft Word. + */ + it('covers every OAuth-connectable service of every registrable app', () => { + const { providers } = loadOAuthConnectCatalog() + + for (const capabilityId of Object.keys(OAUTH_CLIENT_CAPABILITIES)) { + const provider = providers.get(capabilityId) + + expect(provider, capabilityId).toBeDefined() + expect( + provider!.services.some((service) => !service.serviceAccountOnly), + capabilityId + ).toBe(true) + } + }) + + /** + * Service-account services have no OAuth flow, so no app registration covers + * them and listing one would send the reader looking for a client id that + * does not exist. + */ + /** + * One OAuth app is one client id, and more than one connector can be + * registered against it: `manageengine-sdp` reads `ZOHO_CLIENT_ID` despite + * being its own provider entry. Grouping by provider alone left its redirect + * URI and scopes off the page for the app that covers it. + */ + it('groups a connector under the app whose client id it registers against', () => { + const clientIdEnv = loadClientIdEnvByProviderId() + + expect(clientIdEnv.get('manageengine-sdp')).toBe('ZOHO_CLIENT_ID') + expect(clientIdEnv.get('zoho-desk')).toBe('ZOHO_CLIENT_ID') + expect(clientIdEnv.get('sharepoint')).toBe('MICROSOFT_CLIENT_ID') + }) + + it('leaves out services that have no OAuth flow', () => { + const { services } = loadOAuthConnectCatalog() + + expect(services.get('google-service-account')?.serviceAccountOnly).toBe(true) + expect(services.get('snowflake')?.serviceAccountOnly).toBe(true) + expect(services.get('gmail')?.serviceAccountOnly).toBe(false) + }) + + /** + * Only apps with several connectors hoist, and only scopes literally present + * in every one of them. Hoisting a scope one connector does not request would + * tell a reader to over-grant; keeping a scope in a row that every sibling + * also needs is the repetition this exists to remove. + */ + it('hoists only the scopes shared by every connector of an app', () => { + expect( + sharedScopesAcrossConnectors([ + ['openid', 'email', 'Mail.Send'], + ['openid', 'email', 'Files.Read'], + ]) + ).toEqual(['openid', 'email']) + + expect(sharedScopesAcrossConnectors([['openid', 'email']])).toEqual([]) + expect(sharedScopesAcrossConnectors([['openid'], ['email']])).toEqual([]) + }) + + /** + * Salesforce runs a second authorization server for sandboxes under its own + * provider id, and each provider id is its own redirect URI. Listing only the + * primary sends a self-hoster to register half of what they need, and the + * sandbox connect flow then fails redirect-URI validation. + */ + it('lists every provider id a service authenticates through', () => { + const salesforce = loadOAuthConnectCatalog().services.get('salesforce') + + expect(salesforce?.additionalProviderIds).toContain('salesforce-sandbox') + expect(loadOAuthConnectCatalog().services.get('sharepoint')?.additionalProviderIds).toEqual([]) + }) + + it('reads each service display name and provider id', () => { + const { providers } = loadOAuthConnectCatalog() + const microsoft = providers.get('microsoft')! + + expect(microsoft.name).toBe('Microsoft') + expect(microsoft.services.find((s) => s.serviceId === 'sharepoint')?.name).toBe('SharePoint') + expect(microsoft.services.find((s) => s.serviceId === 'gmail')).toBeUndefined() + }) +}) diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 157bd16ed33..e51a6fa9373 100755 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -2,10 +2,19 @@ import fs from 'fs' import path from 'path' import { fileURLToPath, pathToFileURL } from 'url' +import { OAUTH_CLIENT_CAPABILITIES } from '@sim/deployment-config/env-capabilities' import { isVersionedType, stripVersionSuffix } from '@sim/utils/string' import { glob } from 'glob' import type { BlockCategory } from '../apps/sim/blocks/types' import { IntegrationType } from '../apps/sim/blocks/types' +import { + ADDITIONAL_PROVIDER_IDS, + ENV_GATED_SCOPES, + getScopeDescription, + MANAGED_OAUTH_ADDITIONAL_SCOPES, + OAUTH_SCOPES, + SERVICE_SCOPE_NOTES, +} from '../apps/sim/lib/oauth/scopes' import type { ToolOutputProperty } from '../apps/sim/tools/types' /** @@ -42,6 +51,12 @@ const LANDING_INTEGRATIONS_DATA_PATH = path.join( 'apps/sim/app/(landing)/integrations/data' ) const TRIGGERS_PATH = path.join(rootDir, 'apps/sim/triggers') +const OAUTH_CONFIG_PATH = path.join(rootDir, 'apps/sim/lib/oauth/oauth.ts') +const OAUTH_REGISTRATION_PATH = path.join(rootDir, 'apps/sim/lib/auth/connectors/providers.ts') +const SELF_HOSTING_OAUTH_DOC_PATH = path.join( + rootDir, + 'apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx' +) const sourceFileCache = new Map() const sourceGlobCache = new Map>() const blockConfigCache = new Map>() @@ -462,6 +477,18 @@ const emittedByPath = new Map() */ const wouldDeletePaths: string[] = [] +function getErrorText(error: unknown): string { + return error instanceof Error ? error.message : String(error) +} + +/** + * Per-block failures, collected so a run cannot report success after skipping a + * page. {@link generateBlockDoc} catches its own errors so one unreadable block + * does not abandon the other 300, but a swallowed error still means a page went + * missing or stale, and the exit code is what CI reads. + */ +const blockGenerationFailures: string[] = [] + /** Writes a generated artifact, or in check mode records its final content for the end-of-run comparison. */ function emitGeneratedFile(filePath: string, content: string): void { if (CHECK_ONLY) { @@ -1308,6 +1335,437 @@ async function buildToolDescriptionMap(): Promise { return { desc, name } } +/** + * Connect-surface facts about one OAuth service that the `## Scopes` section + * needs but {@link OAUTH_SCOPES} does not carry. + */ +interface OAuthServiceConnectInfo { + /** Key this service is declared under, which is also its {@link OAUTH_SCOPES} key. */ + serviceId: string + /** Provider id the scope labels are keyed by, which differs from the service id for Gmail. */ + providerId: string + /** Display name, used to label the service in the self-hosting reference. */ + name: string + /** Base provider key, which is also the OAuth-client capability id. */ + baseProvider: string + /** + * Further provider ids whose credentials authenticate this same service, + * because the provider runs more than one authorization server. Each is its + * own redirect URI, so a reference listing only the primary sends a + * self-hoster off to register half of what they need. + */ + additionalProviderIds: readonly string[] + /** True for services that have no OAuth flow, so no app registration covers them. */ + serviceAccountOnly: boolean +} + +interface OAuthProviderConnectInfo { + name: string + services: OAuthServiceConnectInfo[] +} + +interface OAuthConnectCatalog { + services: Map + providers: Map +} + +let oauthConnectCatalogCache: OAuthConnectCatalog | null = null + +/** + * Read each OAuth service's `providerId` and whether it accepts a self-issued + * credential out of `lib/oauth/oauth.ts`. + * + * Scraped rather than imported because `OAUTH_PROVIDERS` holds React icon + * components and reads `env` at module load, neither of which survives outside + * Next. Only single-line string properties at the service's own indentation are + * matched, so nested config (`providerIdLabels`) cannot be mistaken for one. + * The service ids found here are checked against {@link OAUTH_SCOPES}, which is + * imported directly, so a service that gains or loses scopes without the other + * side following fails generation instead of quietly publishing nothing. + */ +export function loadOAuthConnectCatalog(): OAuthConnectCatalog { + if (oauthConnectCatalogCache) return oauthConnectCatalogCache + + const services = new Map() + const providers = new Map() + const declaresAdditionalProviderIds = new Set() + const lines = readSourceFile(OAUTH_CONFIG_PATH).split('\n') + + let baseProvider: string | null = null + let serviceId: string | null = null + for (const line of lines) { + const providerMatch = /^ {2}('?)([\w.-]+)\1: \{$/.exec(line) + if (providerMatch) { + baseProvider = providerMatch[2] + serviceId = null + providers.set(baseProvider, { name: baseProvider, services: [] }) + continue + } + if (!baseProvider) continue + + const providerNameMatch = /^ {4}name: '(.+)',$/.exec(line) + if (providerNameMatch) { + providers.get(baseProvider)!.name = providerNameMatch[1] + continue + } + + const serviceMatch = /^ {6}('?)([\w.-]+)\1: \{$/.exec(line) + if (serviceMatch) { + serviceId = serviceMatch[2] + const service: OAuthServiceConnectInfo = { + serviceId, + providerId: serviceId, + name: serviceId, + baseProvider, + additionalProviderIds: [], + serviceAccountOnly: false, + } + services.set(serviceId, service) + providers.get(baseProvider)!.services.push(service) + continue + } + if (!serviceId) continue + + const service = services.get(serviceId)! + const serviceNameMatch = /^ {8}name: '(.+)',$/.exec(line) + if (serviceNameMatch) { + service.name = serviceNameMatch[1] + continue + } + const servicePropviderMatch = /^ {8}providerId: '([^']+)',$/.exec(line) + if (servicePropviderMatch) { + service.providerId = servicePropviderMatch[1] + continue + } + // The value comes from ADDITIONAL_PROVIDER_IDS; this only records that the + // service claims to have extras, so a service that grows a second + // authorization server without the map following is caught below. + if (/^ {8}additionalProviderIds: /.test(line)) { + declaresAdditionalProviderIds.add(serviceId) + continue + } + if (/^ {8}authType: 'service_account',$/.test(line)) { + service.serviceAccountOnly = true + } + } + + for (const [serviceId, additional] of Object.entries(ADDITIONAL_PROVIDER_IDS)) { + const service = services.get(serviceId) + if (service) service.additionalProviderIds = additional + } + + // A redirect URI that goes undocumented is a connect flow that fails for + // whoever picks that authorization server, so the two sides have to agree in + // both directions rather than only where the map happens to have an entry. + const declaredExtras = [...declaresAdditionalProviderIds].sort() + const mappedExtras = Object.keys(ADDITIONAL_PROVIDER_IDS).sort() + if (declaredExtras.join() !== mappedExtras.join()) { + throw new Error( + `Services with additionalProviderIds in lib/oauth/oauth.ts and lib/oauth/scopes.ts ` + + `disagree.\n` + + ` only in oauth.ts: ${declaredExtras.filter((id) => !mappedExtras.includes(id)).join(', ') || 'none'}\n` + + ` only in scopes.ts: ${mappedExtras.filter((id) => !declaredExtras.includes(id)).join(', ') || 'none'}` + ) + } + + const scoped = Object.keys(OAUTH_SCOPES).sort() + const declared = [...services.keys()].sort() + if (scoped.join() !== declared.join()) { + throw new Error( + `OAuth services in lib/oauth/oauth.ts and lib/oauth/scopes.ts disagree.\n` + + ` only in oauth.ts: ${declared.filter((id) => !scoped.includes(id)).join(', ') || 'none'}\n` + + ` only in scopes.ts: ${scoped.filter((id) => !declared.includes(id)).join(', ') || 'none'}` + ) + } + + oauthConnectCatalogCache = { services, providers } + return oauthConnectCatalogCache +} + +/** + * Render the `## Scopes` table for an OAuth integration. + * + * Returns an empty string for API-key and unauthenticated blocks: nothing in + * the repo states what permissions a user-minted key needs, and a heading with + * nothing true under it is worse than no heading. + */ +export function buildScopesSection(serviceId: string | undefined, name: string): string { + if (!serviceId) return '' + + const connectInfo = loadOAuthConnectCatalog().services.get(serviceId) + if (!connectInfo) { + throw new Error( + `Block "${name}" connects through OAuth service "${serviceId}", which no ` + + `OAUTH_PROVIDERS service declares. Its scopes cannot be documented.` + ) + } + + // A handful of providers issue tokens with no scope selection at all + // (Notion, ClickUp, Cal.com, Harmonic, Snowflake, NetSuite). An empty table + // under a "Scopes" heading would read as a generation bug rather than as the + // provider having nothing to choose. + const scopes = OAUTH_SCOPES[serviceId as keyof typeof OAUTH_SCOPES] + if (scopes.length === 0) return '' + + const rows = scopes + .map((scope) => { + const description = getScopeDescription(scope, connectInfo.providerId) + return `| \`${scope}\` | ${description === scope ? '' : escapeMdxCell(description)} |` + }) + .join('\n') + + const gatedNote = renderEnvGatedScopeNote(serviceId, connectInfo.providerId) + + return ( + `## Scopes\n\n` + + `Connecting ${name} through OAuth requests these scopes.\n\n` + + `| Scope | Description |\n| ----- | ----------- |\n${rows}\n` + + (gatedNote ? `\n${gatedNote}\n` : '') + ) +} + +/** + * Providers whose callback path is not `/api/auth/oauth2/callback/`. + * The generated reference states the standard derivation, so an exception has to + * say so where the reader is looking rather than only in the prose below it. + */ +const NON_STANDARD_OAUTH_CALLBACKS: Readonly> = { + trello: + 'Trello is API-key based rather than OAuth 2.0 and calls back to `/api/auth/trello/callback`.', +} + +const SELF_HOSTING_GENERATED_START = '{/* GENERATED-START:oauth-apps */}' +const SELF_HOSTING_GENERATED_END = '{/* GENERATED-END:oauth-apps */}' + +/** + * Which client-id environment variable each connector registration reads. + * + * One OAuth app is one client id, and more than one connector can be + * registered against the same one: `manageengine-sdp` authenticates with + * `ZOHO_CLIENT_ID` even though it is its own provider entry. Grouping the + * reference by provider alone would leave that connector's redirect URI and + * scopes off the page of the app that covers it, and its connect flow would + * fail redirect-URI validation for anyone who registered from the docs. + * + * Read from the registrations themselves, since that is where the runtime + * decides which credentials a connector uses. + */ +export function loadClientIdEnvByProviderId(): Map { + const byProviderId = new Map() + const lines = readSourceFile(OAUTH_REGISTRATION_PATH).split('\n') + + let providerId: string | null = null + for (const line of lines) { + const providerMatch = /^ {6}providerId: '([^']+)',$/.exec(line) + if (providerMatch) { + providerId = providerMatch[1] + continue + } + if (!providerId) continue + + const clientIdMatch = /^ {6}clientId: env\.([A-Z0-9_]+) as string,$/.exec(line) + if (clientIdMatch) { + byProviderId.set(providerId, clientIdMatch[1]) + providerId = null + } + } + + return byProviderId +} + +/** + * Build the per-OAuth-app reference for the self-hosting page. + * + * A self-hoster registers one app per provider and grants it permissions before + * anyone can connect, so they need the scopes grouped by app rather than spread + * over one integration page each. Grouping is by + * {@link OAUTH_CLIENT_CAPABILITIES} — the same client-id/secret pairs the app + * reads at runtime — so an app that backs nine connectors shows all nine. + * + * Scopes stay listed per connector instead of unioned per app. The union would + * tell someone who only wants SharePoint to grant their Entra registration the + * directory-write permissions `microsoft-ad` needs, which is worse advice than + * no advice. + */ +/** + * Sentence naming the scopes a deployment only requests once it sets a flag. + * + * These are deliberately absent from the tables so a generated page cannot vary + * by deployment, which would leave everyone else failing `docs:check`. Saying so + * in prose keeps the page honest for the reader who does set the flag. + */ +function renderEnvGatedScopeNote(serviceId: string, providerId: string): string { + const notes: string[] = [] + + const managed = MANAGED_OAUTH_ADDITIONAL_SCOPES[providerId] + if (managed) { + notes.push( + `A managed connection also requests ${renderScopeList(managed)}, which is how Sim ` + + `verifies the account behind the credential.` + ) + } + + const gated = ENV_GATED_SCOPES[serviceId as keyof typeof ENV_GATED_SCOPES] + if (gated) { + notes.push( + `With \`${gated.envVar}\` set, Sim also requests ${renderScopeList(gated.scopes)}. ` + + `Add them to the app as well, or leave the flag unset.` + ) + } + + const caveat = SERVICE_SCOPE_NOTES[serviceId as keyof typeof SERVICE_SCOPE_NOTES] + if (caveat) notes.push(caveat) + + return notes.join(' ') +} + +const renderScopeList = (scopes: readonly string[]): string => + scopes.map((scope) => `\`${scope}\``).join(', ') + +/** + * Scopes that every connector of one app requests. + * + * Hoisting these above the table earns its keep only because the repetition is + * severe where it happens: all 15 Google connectors carry the two `userinfo` + * URLs, and all 9 Microsoft ones carry `openid`, `profile`, `email` and + * `offline_access`. Derived from the scope lists rather than hardcoded per + * provider, so an app whose connectors stop agreeing simply stops hoisting. + * + * Returns nothing for a single-connector app, where "every connector below also + * requests" would read as a second requirement rather than a shared one. + */ +export function sharedScopesAcrossConnectors(scopeLists: readonly (readonly string[])[]): string[] { + if (scopeLists.length < 2) return [] + const [first, ...rest] = scopeLists + return first.filter((scope) => rest.every((list) => list.includes(scope))) +} + +function buildSelfHostingOAuthReference(): string { + const { providers, services } = loadOAuthConnectCatalog() + + const clientIdEnvByProviderId = loadClientIdEnvByProviderId() + const apps = Object.keys(OAUTH_CLIENT_CAPABILITIES) + .map((capabilityId) => { + const provider = providers.get(capabilityId) + if (!provider) { + throw new Error( + `OAuth client capability "${capabilityId}" has no matching provider in ` + + `lib/oauth/oauth.ts, so its app registration cannot be documented.` + ) + } + + // Connectors registered against this app's client id from some other + // provider entry. Additive rather than a replacement for the grouping + // above, because four capabilities (Instagram, Salesforce, Shopify, + // Trello) run custom flows that declare no `clientId: env.X` line. + const [clientIdEnv] = + OAUTH_CLIENT_CAPABILITIES[capabilityId as keyof typeof OAUTH_CLIENT_CAPABILITIES] + const own = new Set(provider.services.map((service) => service.serviceId)) + const sharedConnectors = [...services.values()].filter( + (service) => + !own.has(service.serviceId) && + !service.serviceAccountOnly && + clientIdEnvByProviderId.get(service.providerId) === clientIdEnv + ) + + return { capabilityId, provider, sharedConnectors } + }) + .sort((a, b) => compareCatalogNames(a.provider.name, b.provider.name)) + + const sections = apps.map(({ capabilityId, provider, sharedConnectors }) => { + const envVars = OAUTH_CLIENT_CAPABILITIES[ + capabilityId as keyof typeof OAUTH_CLIENT_CAPABILITIES + ] + .map((name) => `\`${name}\``) + .join(' / ') + + const connectors = [...provider.services, ...sharedConnectors] + .filter((service) => !service.serviceAccountOnly) + .map((service) => ({ + service, + scopes: OAUTH_SCOPES[service.serviceId as keyof typeof OAUTH_SCOPES] ?? [], + })) + + const shared = sharedScopesAcrossConnectors(connectors.map(({ scopes }) => scopes)) + const sharedLine = shared.length + ? `Every connector below also requests ${renderScopeList(shared)}.\n\n` + : '' + + const rows = connectors + .map(({ service, scopes }) => { + const specific = scopes.filter((scope) => !shared.includes(scope)) + const rendered = specific.length + ? renderScopeList(specific) + : scopes.length + ? 'The shared scopes above, nothing further' + : 'None to select' + const providerIds = [service.providerId, ...service.additionalProviderIds] + .map((id) => `\`${id}\``) + .join('
') + return `| ${escapeMdxCell(service.name)} | ${providerIds} | ${rendered} |` + }) + .join('\n') + + // Deduplicated: a note that applies to every connector of an app (Google's + // managed `openid`) is one sentence about the app, not the same sentence + // fifteen times. + const gatedNotes = [ + ...new Set( + connectors + .map(({ service }) => renderEnvGatedScopeNote(service.serviceId, service.providerId)) + .filter(Boolean) + ), + ].join(' ') + + const callbackNote = NON_STANDARD_OAUTH_CALLBACKS[capabilityId] + + return ( + `### ${provider.name}\n\n` + + `${envVars}\n\n` + + sharedLine + + `| Connector | Provider ID | Scopes to grant |\n| --- | --- | --- |\n${rows}\n` + + (gatedNotes ? `\n${gatedNotes}\n` : '') + + (callbackNote ? `\n${callbackNote}\n` : '') + ) + }) + + // Says once, at the top, what every table below is: the OAuth flow's request. + // Without it a reader who connects with a Google service account instead reads + // the identity scopes as something to grant, and `getServiceAccountToken` + // strips exactly those from that token. + const preamble = + `Each table is what the OAuth flow requests, so it is what the app registration has to ` + + `allow. A credential someone issues themselves instead (a service account, a pasted API ` + + `token) carries its own access, which these do not govern.\n` + + return `${preamble}\n${sections.join('\n')}` +} + +/** + * Replace the generated block on the self-hosting OAuth page in place. + * + * The page is hand-written apart from this block, so the markers are required + * rather than created: silently appending a second copy of the reference is a + * worse failure than stopping and saying the markers are gone. + */ +function writeSelfHostingOAuthReference(): void { + const existing = readSourceFile(SELF_HOSTING_OAUTH_DOC_PATH) + const start = existing.indexOf(SELF_HOSTING_GENERATED_START) + const end = existing.indexOf(SELF_HOSTING_GENERATED_END) + if (start === -1 || end === -1 || end < start) { + throw new Error( + `${SELF_HOSTING_OAUTH_DOC_PATH} is missing the ` + + `${SELF_HOSTING_GENERATED_START} / ${SELF_HOSTING_GENERATED_END} markers.` + ) + } + + const next = + `${existing.slice(0, start + SELF_HOSTING_GENERATED_START.length)}\n\n` + + `${buildSelfHostingOAuthReference()}\n${existing.slice(end)}` + + emitGeneratedFile(SELF_HOSTING_OAUTH_DOC_PATH, next) +} + /** * Detect the authentication type from block content. * Returns 'oauth' if the block uses oauth-input credentials, @@ -4102,62 +4560,74 @@ async function generateBlockDoc(blockPath: string) { continue } - if ( - blockConfig.type.includes('_trigger') || - blockConfig.type.includes('_webhook') || - blockConfig.type.includes('rss') - ) { - console.log(`Skipping ${blockConfig.type} - contains '_trigger'`) - continue - } + try { + if ( + blockConfig.type.includes('_trigger') || + blockConfig.type.includes('_webhook') || + blockConfig.type.includes('rss') + ) { + console.log(`Skipping ${blockConfig.type} - contains '_trigger'`) + continue + } - if ( - (blockConfig.category === 'blocks' && - !NATIVE_RESOURCE_BLOCK_TYPES.has(stripVersionSuffix(blockConfig.type))) || - blockConfig.type === 'sim_workspace_event' || - blockConfig.type === 'evaluator' || - blockConfig.type === 'number' || - blockConfig.type === 'webhook' || - blockConfig.type === 'schedule' || - blockConfig.type === 'mcp' || - blockConfig.type === 'generic_webhook' || - blockConfig.type === 'rss' - ) { - continue - } + if ( + (blockConfig.category === 'blocks' && + !NATIVE_RESOURCE_BLOCK_TYPES.has(stripVersionSuffix(blockConfig.type))) || + blockConfig.type === 'sim_workspace_event' || + blockConfig.type === 'evaluator' || + blockConfig.type === 'number' || + blockConfig.type === 'webhook' || + blockConfig.type === 'schedule' || + blockConfig.type === 'mcp' || + blockConfig.type === 'generic_webhook' || + blockConfig.type === 'rss' + ) { + continue + } - // Use stripped type for file name (removes _v2, _v3 suffixes for cleaner URLs) - const displayType = stripVersionSuffix(blockConfig.type) - const outputFilePath = path.join(DOCS_OUTPUT_PATH, `${displayType}.mdx`) + // Use stripped type for file name (removes _v2, _v3 suffixes for cleaner URLs) + const displayType = stripVersionSuffix(blockConfig.type) + const outputFilePath = path.join(DOCS_OUTPUT_PATH, `${displayType}.mdx`) - const existingContent = readGeneratedFile(outputFilePath) + const existingContent = readGeneratedFile(outputFilePath) - const manualSections = existingContent ? extractManualContent(existingContent) : {} + const manualSections = existingContent ? extractManualContent(existingContent) : {} - const markdown = await generateMarkdownForBlock(blockConfig, displayType) + const markdown = await generateMarkdownForBlock(blockConfig, displayType, fileContent) - let finalContent = markdown - if (Object.keys(manualSections).length > 0) { - finalContent = mergeWithManualContent(markdown, existingContent, manualSections) - } + let finalContent = markdown + if (Object.keys(manualSections).length > 0) { + finalContent = mergeWithManualContent(markdown, existingContent, manualSections) + } - emitGeneratedFile(outputFilePath, finalContent) - if (!CHECK_ONLY) { - const logType = - displayType !== blockConfig.type - ? `${displayType} (from ${blockConfig.type})` - : displayType - console.log(`✓ Generated docs for ${logType}`) + emitGeneratedFile(outputFilePath, finalContent) + if (!CHECK_ONLY) { + const logType = + displayType !== blockConfig.type + ? `${displayType} (from ${blockConfig.type})` + : displayType + console.log(`✓ Generated docs for ${logType}`) + } + } catch (error) { + // A file can declare more than one block config (a v1 beside a v2), so + // one unusable config must not skip the ones after it. The run still + // exits nonzero on the collected failure. + console.error(`Error processing ${blockConfig.type} in ${blockPath}:`, error) + blockGenerationFailures.push( + `${path.relative(rootDir, blockPath)} (${blockConfig.type}): ${getErrorText(error)}` + ) } } } catch (error) { console.error(`Error processing ${blockPath}:`, error) + blockGenerationFailures.push(`${path.relative(rootDir, blockPath)}: ${getErrorText(error)}`) } } async function generateMarkdownForBlock( blockConfig: BlockConfig, - displayType?: string + displayType?: string, + fileContent?: string ): Promise { const { type, @@ -4296,6 +4766,11 @@ async function generateMarkdownForBlock( usageInstructions = `## Usage Instructions\n\n${longDescription}\n\n` } + const scopesSection = + fileContent && extractAuthType(fileContent) === 'oauth' + ? buildScopesSection(extractOAuthServiceId(fileContent), name) + : '' + return `--- title: ${name} description: ${description} @@ -4309,7 +4784,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" /> ${usageInstructions} - +${scopesSection} ${toolsSection} ` } @@ -4969,6 +5444,7 @@ async function generateAllBlockDocs() { await writeIntegrationsJson(visibleIconMapping) writeIntegrationsIconMapping(visibleIconMapping) + writeSelfHostingOAuthReference() // Compute the canonical set of tool docs and clean up anything stale — // covers hidden blocks AND blocks re-categorized away from `'tools'`. @@ -5026,6 +5502,13 @@ if (import.meta.main) { console.error('Documentation generation failed') process.exit(1) } + if (blockGenerationFailures.length > 0) { + console.error( + `Documentation generation failed for ${blockGenerationFailures.length} block(s):\n` + + `- ${blockGenerationFailures.join('\n- ')}` + ) + process.exit(1) + } if (CHECK_ONLY) { const genuinelyDeleted = wouldDeletePaths .filter((docPath) => !emittedByPath.has(docPath))