Skip to content

Commit 6b6b655

Browse files
committed
revert(outlook): drop Calendars.ReadWrite.Shared from the outlook provider
Reverts the scope I added two rounds ago. It was the wrong call. This provider is shared by work/school 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, breaking functionality that works today. The PR already documents this exact reasoning as why findMeetingTimes was excluded, and that decision was made against a live personal mailbox. The evidence I added it on was a summarized read of the permissions reference claiming MSA support; a targeted follow-up could not confirm it for Calendars.ReadWrite.Shared specifically. Given the asymmetry — broken consent for all personal users vs. a shared-calendar feature gap — least privilege wins. Calendar operations therefore target calendars the account owns. The calendarId param descriptions now say a calendar shared by another user may return 403, and the scope list carries a comment explaining why .Shared must not be re-added.
1 parent a42782d commit 6b6b655

6 files changed

Lines changed: 24 additions & 19 deletions

File tree

apps/docs/content/docs/en/integrations/outlook.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ List Outlook calendar events within a start/end time window
481481

482482
| Parameter | Type | Required | Description |
483483
| --------- | ---- | -------- | ----------- |
484-
| `calendarId` | string | No | ID of the calendar to read. Defaults to the mailbox default calendar. |
484+
| `calendarId` | string | No | ID of the calendar to read. Defaults to the mailbox default calendar. Calendars shared by another user are not supported and may return 403. |
485485
| `startDateTime` | string | No | Start of the time window \(ISO 8601, e.g. 2025-06-03T00:00:00-08:00\). Interpreted as UTC if no offset is given. Required unless paging with pageToken. |
486486
| `endDateTime` | string | No | End of the time window \(ISO 8601, e.g. 2025-06-10T00:00:00-08:00\). Interpreted as UTC if no offset is given. Required unless paging with pageToken. |
487487
| `maxResults` | number | No | Maximum number of events to return per page \(default: 10, max: 100\) |
@@ -565,7 +565,7 @@ Create a new Outlook calendar event
565565

566566
| Parameter | Type | Required | Description |
567567
| --------- | ---- | -------- | ----------- |
568-
| `calendarId` | string | No | ID of the calendar to create the event in. Defaults to the default calendar. |
568+
| `calendarId` | string | No | ID of the calendar to create the event in. Defaults to the mailbox default calendar. Calendars shared by another user are not supported and may return 403. |
569569
| `subject` | string | Yes | Event subject/title |
570570
| `startDateTime` | string | Yes | Start time \(ISO 8601, e.g. 2025-06-03T10:00:00-08:00\). A date-only value \(2025-06-03\) for both start and end creates an all-day event. |
571571
| `endDateTime` | string | Yes | End time \(ISO 8601, e.g. 2025-06-03T11:00:00-08:00\). A date-only value \(2025-06-04\) for both start and end creates an all-day event. |

apps/sim/lib/oauth/oauth.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -397,19 +397,22 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
397397
icon: OutlookIcon,
398398
baseProviderIcon: MicrosoftIcon,
399399
/**
400-
* Calendar scopes back the Outlook calendar operations. Both are required, and
401-
* neither subsumes the other:
402-
* - `Calendars.ReadWrite` is the only permission Graph accepts for creating and
403-
* updating events and for accept / tentativelyAccept / decline, which document
404-
* it as least-privileged with "Higher: Not available".
405-
* - `Calendars.ReadWrite.Shared` additionally covers calendars other users have
406-
* shared with (or delegated to) the account. The calendar picker lists
407-
* `/me/calendars`, which can include those, so without it selecting a shared
408-
* team calendar would 403 on read and write.
400+
* `Calendars.ReadWrite` backs the Outlook calendar operations. Graph documents it
401+
* as the sole accepted permission for creating and updating events and for
402+
* accept / tentativelyAccept / decline ("Higher: Not available"), and it is
403+
* supported for both work/school and personal Microsoft accounts.
404+
*
405+
* Do NOT add `Calendars.ReadWrite.Shared` here. This provider is shared by work
406+
* and personal Outlook accounts, and the `.Shared` calendar scopes are not
407+
* confirmed supported for personal Microsoft accounts — requesting one risks
408+
* failing consent for personal users, which would take mail access down with it.
409+
* That is the same reasoning that kept `findMeetingTimes` out of this integration.
410+
* The consequence is that calendar operations target calendars the account owns;
411+
* picking a calendar shared by another user may return 403 from Graph.
409412
*
410413
* Microsoft only grants newly-added scopes on a fresh authorization, so users who
411-
* connected Outlook before these scopes existed must reconnect (re-consent) their
412-
* account before the calendar operations will work.
414+
* connected Outlook before `Calendars.ReadWrite` existed must reconnect
415+
* (re-consent) before the calendar operations will work.
413416
*
414417
* @see https://learn.microsoft.com/en-us/graph/permissions-reference
415418
*/
@@ -422,7 +425,6 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
422425
'Mail.Read',
423426
'Mail.Send',
424427
'Calendars.ReadWrite',
425-
'Calendars.ReadWrite.Shared',
426428
'offline_access',
427429
],
428430
},

apps/sim/lib/oauth/utils.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,9 @@ describe('getCanonicalScopesForProvider', () => {
336336
expect(outlookScopes.length).toBeGreaterThan(0)
337337
expect(outlookScopes).toContain('Mail.ReadWrite')
338338
expect(outlookScopes).toContain('Calendars.ReadWrite')
339-
expect(outlookScopes).toContain('Calendars.ReadWrite.Shared')
339+
// .Shared is deliberately absent: unconfirmed for personal MSA, and this provider
340+
// serves personal accounts whose mail access would break if consent failed.
341+
expect(outlookScopes).not.toContain('Calendars.ReadWrite.Shared')
340342

341343
const excelScopes = getCanonicalScopesForProvider('microsoft-excel')
342344

@@ -621,7 +623,7 @@ describe('getScopesForService', () => {
621623
expect(scopes.length).toBeGreaterThan(0)
622624
expect(scopes).toContain('Mail.ReadWrite')
623625
expect(scopes).toContain('Calendars.ReadWrite')
624-
expect(scopes).toContain('Calendars.ReadWrite.Shared')
626+
expect(scopes).not.toContain('Calendars.ReadWrite.Shared')
625627
})
626628

627629
it.concurrent('should return empty array for empty string', () => {

apps/sim/lib/oauth/utils.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ export const SCOPE_DESCRIPTIONS: Record<string, string> = {
238238
'Mail.Read': 'Read Microsoft emails',
239239
'Mail.Send': 'Send emails',
240240
'Calendars.ReadWrite': 'Read and manage Outlook calendar events',
241-
'Calendars.ReadWrite.Shared': 'Read and manage shared Outlook calendars',
242241
'Files.Read': 'Read OneDrive files',
243242
'Files.ReadWrite': 'Read and write OneDrive files',
244243
'Tasks.ReadWrite': 'Read and manage Planner tasks',

apps/sim/tools/outlook/calendar_create_event.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export const outlookCalendarCreateEventTool: ToolConfig<
4646
type: 'string',
4747
required: false,
4848
visibility: 'user-only',
49-
description: 'ID of the calendar to create the event in. Defaults to the default calendar.',
49+
description:
50+
'ID of the calendar to create the event in. Defaults to the mailbox default calendar. Calendars shared by another user are not supported and may return 403.',
5051
},
5152
subject: {
5253
type: 'string',

apps/sim/tools/outlook/calendar_list_events.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ export const outlookCalendarListEventsTool: ToolConfig<
4343
type: 'string',
4444
required: false,
4545
visibility: 'user-only',
46-
description: 'ID of the calendar to read. Defaults to the mailbox default calendar.',
46+
description:
47+
'ID of the calendar to read. Defaults to the mailbox default calendar. Calendars shared by another user are not supported and may return 403.',
4748
},
4849
// Not `required`, because a paging call supplies only `pageToken` and the window bounds
4950
// are baked into the nextLink. The url builder enforces "pageToken OR both bounds".

0 commit comments

Comments
 (0)