Skip to content

feat(oauth): publish integration scopes in docs, and fix the Excel SharePoint scopes - #7440

Open
mzxchandra wants to merge 7 commits into
stagingfrom
feat/oauth-scopes-docs
Open

feat(oauth): publish integration scopes in docs, and fix the Excel SharePoint scopes#7440
mzxchandra wants to merge 7 commits into
stagingfrom
feat/oauth-scopes-docs

Conversation

@mzxchandra

Copy link
Copy Markdown
Contributor

Summary

Docs never said what an OAuth connection actually grants. A user hit the consent screen cold, and a self-hoster registering an app had to guess which permissions to add before anyone could connect. This publishes the real scope sets, straight from the config the runtime uses, and fixes an Excel scope gap found while doing it.

Three commits, each independently valid and docs:check-clean:

1. refactor(oauth) — extract scope data into a dependency-free module. OAUTH_PROVIDERS imports ~120 React icons and reads env at module load, so nothing outside Next can read the scopes it declares. Moved the 61 scope arrays out of oauth.ts, and the label tables plus getScopeDescription out of utils.ts, into lib/oauth/scopes.ts with zero imports. Behavior-preserving: all 61 scope lists verified identical to staging (the one intentional difference is Excel, in commit 3).

2. feat(docs) — publish the scopes. A ## Scopes table on 51 integration pages, plus a generated per-app reference on the self-hosting page (env vars, provider IDs, scopes grouped by connector).

3. fix(excel) — request the SharePoint scopes the file picker needs. See below.

The Excel bug

The Excel block exposes a SharePoint site picker (blocks/blocks/microsoft_excel.ts:795) and a drive picker that calls GET /sites/{id}/drives, and builds /drives/{driveId}/items/{id} for arbitrary drives. But the integration only requested Files.Read / Files.ReadWrite, which cover the signed-in user's own OneDrive. A workbook in a document library returned insufficient privileges.

Added Files.Read.All, Files.ReadWrite.All, Sites.Read.All. Word already carries the first two for exactly this reason. All are user-consentable, so Excel does not move behind admin consent.

Existing connections must reconnect (Microsoft only grants new scopes on fresh authorization). No migration needed: the block's credential selector already derives requiredScopes from getScopesForService('microsoft-excel'), so stale connections show as needing an update on their own.

Drift this replaces

The hand-maintained self-hosting tables were already wrong:

  • The Microsoft provider-ID list was missing microsoft-word. Anyone following it registered an app that could not connect Word. (Staging hand-patched this and the FAQ counts while this branch was in flight; the generated block supersedes both.)
  • The page claimed scopes "generally do not need to pre-declare" — false for Entra, where every delegated permission must be on the registration and the microsoft-ad set needs tenant admin consent.

Design notes

  • Scopes stay per connector, not unioned per app. A union would tell someone who only wants SharePoint to grant their Entra registration the directory-write permissions microsoft-ad needs.
  • Scopes shared by every connector of an app are hoisted above the table, derived from the data rather than hardcoded. Removes 30 repeated Google URLs and 36 Microsoft ones.
  • 6 services are skipped (Notion, ClickUp, Cal.com, Harmonic, Snowflake, NetSuite) because they declare no scopes; a heading over an empty table reads as a generation bug.
  • Every scope row has a description. Two Zoho Desk scopes had none, so they were added.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • bun run check:audits — 45/45 pass, including docs:check
  • bun run type-check — clean
  • 297 tests across lib/oauth, the Microsoft blocks, and the connect modal
  • 82 generator tests in scripts/generate-docs.test.ts
  • docs:check verified green at each of the three commits independently
  • Drift guards verified by tampering: editing a generated row fails docs:check, removing the page markers fails generation

New tests cover the module split (the two files must declare the same services, and every table scope must actually be requested), the generated section (env-flag scopes excluded so output cannot vary by deployment, empty-scope services skipped, unknown service throws), and the shared-scope hoisting rule.

Reviewers should focus on: the scope diff in scopes.ts for commit 3 (the only intended behavior change), and whether the per-connector grouping on the self-hosting page is the right call versus a union.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

Integration page (SharePoint) and the self-hosting reference (Google, showing hoisted shared scopes) render as ordinary doc tables; verified locally at localhost:3001.

@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Sep 3, 2026 9:08pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR extracts OAuth scope metadata into a dependency-free catalog, publishes generated scope documentation, and expands Microsoft Excel permissions for SharePoint-backed workbooks.

  • Centralizes connector scopes and descriptions in lib/oauth/scopes.ts.
  • Generates per-integration and self-hosting OAuth scope references.
  • Adds Microsoft Graph file and site permissions required by the Excel SharePoint picker.
  • Updates OAuth modal behavior and adds scope-generation coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/oauth/scopes.ts Introduces the readonly canonical scope and description catalog, including the expanded Excel SharePoint permissions.
apps/sim/lib/oauth/oauth.ts Replaces inline scope arrays with copied entries from the central catalog and uses the established absolute import alias.
scripts/generate-docs.ts Generates connector-specific OAuth scope documentation and shared provider metadata from the canonical catalog.
apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx Aligns connection-scope handling with the extracted catalog and environment-gated scope data.
apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx Publishes generated provider registration details and connector-specific OAuth permissions for self-hosters.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Catalog[OAuth scope catalog] --> Runtime[OAuth provider configuration]
  Catalog --> Generator[Documentation generator]
  Generator --> IntegrationDocs[Integration scope tables]
  Generator --> SelfHostingDocs[Self-hosting OAuth reference]
  Runtime --> Consent[Provider consent request]
  Excel[Microsoft Excel connector] --> Runtime
Loading

Reviews (5): Last reviewed commit: "fix(docs): make the generation guard act..." | Re-trigger Greptile

Comment thread apps/sim/lib/oauth/oauth.ts Outdated
Comment thread apps/sim/lib/oauth/scopes.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 62 files

Re-trigger cubic

Comment thread apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx
Comment thread apps/docs/content/docs/integrations/wealthbox.mdx Outdated
Comment thread apps/docs/content/docs/integrations/pipedrive.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_sheets.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_tasks.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_slides.mdx Outdated
Comment thread apps/docs/content/docs/integrations/zoho_desk.mdx Outdated
Comment thread apps/sim/lib/oauth/scopes.ts Outdated
Comment thread apps/sim/lib/oauth/scopes.ts Outdated
Comment thread apps/docs/content/docs/integrations/monday.mdx Outdated
@mzxchandra
mzxchandra force-pushed the feat/oauth-scopes-docs branch from e4b7842 to cd9794c Compare September 3, 2026 20:17
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@mzxchandra I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 62 files

Re-trigger cubic

Comment thread apps/docs/content/docs/integrations/google_slides.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_contacts.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_calendar.mdx Outdated
Comment thread apps/docs/content/docs/integrations/pipedrive.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_drive.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_forms.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_bigquery.mdx Outdated
Comment thread apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx
Comment thread apps/docs/content/docs/integrations/google_tasks.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_meet.mdx Outdated
@mzxchandra
mzxchandra force-pushed the feat/oauth-scopes-docs branch from cd9794c to b2c817e Compare September 3, 2026 20:34
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@mzxchandra I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 63 files

Re-trigger cubic

Comment thread apps/docs/content/docs/integrations/asana.mdx Outdated
Comment thread apps/docs/content/docs/integrations/monday.mdx Outdated
Comment thread apps/docs/content/docs/integrations/hubspot.mdx Outdated
Comment thread apps/docs/content/docs/integrations/airtable.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_bigquery.mdx Outdated
Comment thread apps/docs/content/docs/integrations/webflow.mdx Outdated
Comment thread apps/docs/content/docs/integrations/salesforce.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_sheets.mdx Outdated
Comment thread apps/docs/content/docs/integrations/zoom.mdx Outdated
Comment thread apps/docs/content/docs/integrations/gmail.mdx Outdated
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@mzxchandra I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 62 files

Re-trigger cubic

Comment thread apps/docs/content/docs/integrations/google_bigquery.mdx Outdated
Comment thread apps/docs/content/docs/integrations/microsoft_ad.mdx Outdated
Comment thread apps/docs/content/docs/integrations/pipedrive.mdx Outdated
Comment thread apps/docs/content/docs/integrations/google_slides.mdx Outdated
Comment thread scripts/generate-docs.ts
Comment thread apps/docs/content/docs/integrations/microsoft_teams.mdx Outdated
`OAUTH_PROVIDERS` imports ~120 React icon components and reads `env` at
module load, so nothing outside Next can read the scope sets it declares.
That blocks the docs generator from publishing what each integration
requests, and it buries a security-relevant surface inside a file that
also owns token refresh and error redaction.

Move the 61 per-service scope arrays out of `oauth.ts`, and the scope
label tables plus `getScopeDescription` out of `utils.ts`, into a new
`lib/oauth/scopes.ts` with no imports at all. `oauth.ts` composes from it
(`scopes: [...OAUTH_SCOPES.sharepoint]`), and Slack's approval-gated
extras stay appended there because whether they are requested depends on
an environment flag.

Behavior-preserving: every one of the 61 scope lists was verified
identical before and after the move. Consumers of the moved exports are
repointed at the new module rather than re-exported through `utils.ts`,
which the import rules forbid.

The new test asserts the two files cover exactly the same services and
that every scope in the table is actually requested at authorization
time, so the split cannot silently drift.
Nothing in the docs said what a connection grants. A user hit the consent
screen cold, and a self-hoster registering an app had to guess which
permissions to add before anyone could connect.

Generate a `## Scopes` table on every OAuth integration page from
`OAUTH_SCOPES`, labelled with the descriptions the connect modal already
uses. 51 pages get one. The 6 services that declare no scopes (Notion,
ClickUp, Cal.com, Harmonic, Snowflake, NetSuite) are skipped, because a
heading over an empty table reads as a generation bug rather than as the
provider having nothing to choose. Services that also accept a
self-issued credential say the token needs the same access.

Replace the hand-maintained provider tables on the self-hosting page with
a generated per-app reference: env vars, provider IDs, and scopes grouped
by connector. Scopes stay per connector rather than unioned per app,
because a union would tell someone who only wants SharePoint to grant
their Entra registration the directory-write permissions `microsoft-ad`
needs. Scopes shared by every connector of an app are hoisted above the
table, which removes 30 repeated Google URLs and 36 Microsoft ones.

The hand-maintained version had already drifted: its Microsoft list was
missing `microsoft-word`, so a self-hoster following it registered an app
that could not connect Word. It also claimed scopes need no
pre-declaration, which is wrong for Entra, where every delegated
permission must be on the registration and the `microsoft-ad` set needs
tenant admin consent.

Generation now fails loudly if `oauth.ts` and `scopes.ts` stop agreeing on
the service list, if a block points at an unknown OAuth service, or if the
self-hosting page loses its generated-block markers. `bun run docs:check`
covers all of it.
The Excel block exposes a SharePoint site picker and a drive picker, and
builds `/drives/{driveId}/items/{id}` for arbitrary drives, but the
integration only requested `Files.Read` and `Files.ReadWrite`. Those cover
the signed-in user's own OneDrive, so a workbook in a document library
came back as insufficient privileges and the drive picker's
`GET /sites/{id}/drives` call failed outright.

Add `Files.Read.All`, `Files.ReadWrite.All` and `Sites.Read.All`. Word
already carries the first two for exactly this reason. All are
user-consentable, so this does not push Excel behind admin consent, and
none grants access to anything the signed-in account could not already
open.

Microsoft only grants newly-added scopes on a fresh authorization, so
existing connections must reconnect. No migration needed: the block's
credential selector already computes `requiredScopes` from
`getScopesForService('microsoft-excel')`, so those connections now show
as needing an update on their own.

The shared `Files.*` labels name OneDrive, which reads as the wrong
product on the Excel consent screen, so add Excel-specific wording
alongside the Word overrides that solve the same problem.
… grants

Review findings from the first round, all in the generated prose rather
than the scope data.

Only some self-issued credentials have permissions to pick. An Airtable
PAT and a Slack custom bot do; a Wealthbox, Monday or Pipedrive token
carries its creating user's access with no picker at all, and a Google
service account uses domain-wide delegation over a set the JWT flow
filters. Telling every reader to "grant it the same access" sent most of
them looking for a setting that does not exist. Say instead that a
self-issued credential carries whatever access it was created with.

Slack's approval-gated scopes were invisible to a self-hoster. They stay
out of the tables so generated output cannot vary by deployment, but
anyone who sets `SLACK_EXTENDED_SCOPES` has to add them to the app first,
and Slack rejects the entire authorization when the app is not approved
for one. Both the integration page and the self-hosting reference now
name them and their flag, sourced from a new `ENV_GATED_SCOPES` entry that
`getSlackApprovalGatedScopes` also reads, so the prose cannot drift from
the grant.

`Chat.ReadWrite` and `Mail.ReadWrite` were labelled write-only. Both also
read, and these labels show on the consent screen as well as in the docs.

Also switch the scopes import in `oauth.ts` to the absolute specifier its
sibling OAuth imports use.
Round two returned the same finding on 14 pages: the one sentence covering
self-issued credentials still read as if the scope table were something to
apply to them. It cannot be one sentence, because the credential models
differ in what is true about them.

A Google service account is the sharp case. Its access comes from the key
and the domain-wide delegation granted in Workspace, and
`getServiceAccountToken` strips the two `userinfo` scopes from the JWT
because Google rejects user-identity scopes for a service account. Naming
those scopes beside that credential describes access it can never hold.
A pasted API token is the opposite: most have no permission picker at all
and simply carry their creating account's access, so any instruction to
grant something sends the reader looking for a setting that does not
exist.

Carry the `serviceAccountProviderId` through the docs catalog instead of a
boolean, and pick the sentence from it. Neither variant tells the reader to
match the table.

The excluded identity scopes now live in `scopes.ts` and
`credential-service.ts` imports them, so the published claim and the filter
that makes it true cannot drift apart.

The self-hosting reference gains one line saying what every table on it is:
the OAuth flow's request, and so what the app registration must allow. A
credential issued by hand is not governed by it.
Three review rounds landed on this one sentence, each asking for accuracy
the generated form cannot carry. The models genuinely differ: an Airtable
PAT and a HubSpot private app are limited by scopes chosen at creation, a
Monday token tracks its creator's current permissions rather than a
snapshot, an Attio key uses workspace-admin-selected scopes, a Webflow
token is bound to one site, Salesforce runs as a configured Run As user,
Zoom is a Marketplace app with its own grant, and a Google service account
depends on IAM with delegation only when impersonating. Twenty-odd models
across thirty-four pages is per-provider prose, and inventing it from a
scope table would publish guesses.

Remove the sentence. The scope tables are what this PR is for, and they
stay exactly as they were. The one general statement worth making survives
where it is true and where a reader configuring credentials will meet it:
the self-hosting reference already says each table is the OAuth flow's
request and that a hand-issued credential carries its own access, which
those tables do not govern.

Reverts the catalog's `serviceAccountProviderId` field and the
`SERVICE_ACCOUNT_EXCLUDED_SCOPES` move along with it, since nothing reads
either once the note is gone.
… labels

`generateBlockDoc` catches its own errors so one unreadable block does not
abandon the other 300, which meant the unknown-OAuth-service guard added
here logged and let the run exit 0. A missing page and a successful build
is the worst pairing of the two, and CI reads the exit code. Per-block
failures are now collected and reported at the end, so the run exits 1 and
names the block. Verified by pointing a block at a nonexistent service:
exit 1, block named. Individual blocks still do not abort the whole pass.

`Connecting X requests these scopes` overstated its own table: a service
account or a pasted API token connects the same integration without that
flow and is not governed by it. Now `Connecting X through OAuth`.

`ChannelMessage.Send` said "Write to Microsoft channels", which reads as
general channel write rather than sending a message.
`AppRoleAssignment.ReadWrite.All` omitted the read half of the grant. Both
show on the consent screen as well as in the docs.
@mzxchandra
mzxchandra force-pushed the feat/oauth-scopes-docs branch from 7d41b26 to 69e59f6 Compare September 3, 2026 21:06
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@mzxchandra I have started the AI code review. It will take a few minutes to complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant