Skip to content

feat(msmail): sync shared and delegated mailboxes over Microsoft Graph - #1

Merged
arcaputo3 merged 1 commit into
mainfrom
tjc-3129-msgvault-graph-shared-mailbox
Oct 5, 2026
Merged

arcaputo3 merged 1 commit into
mainfrom
tjc-3129-msgvault-graph-shared-mailbox

Conversation

@arcaputo3

Copy link
Copy Markdown

Problem

Graph mail sync (add-o365 --graph) reads only the signed-in user's own mailbox: every request goes to /me, the token asks only for Mail.Read, and add-o365 requires the account address to match the signed-in identity. A shared mailbox, or another user's mailbox the user has delegated access to, cannot be archived.

Behavior

msgvault add-o365 team@example.com --graph --as you@example.com
msgvault sync team@example.com
  • --as signs in as the named user and verifies the ID token is theirs; signing in as the mailbox itself is refused. The grant adds Mail.Read.Shared.
  • The account is the shared mailbox. Its token (the user's) is saved as tokens/msmail_<mailbox>.json, and the source's sync_config records {"signed_in_as": "<user>"}.
  • Sync uses /users/<mailbox>/… for folders, delta walks and $value downloads. Delta links Graph returns keep that path, so incremental sync is unchanged.
  • Re-adding without --as (or with --as naming the mailbox) turns the account back into an own-mailbox account.
  • delete-staged refuses shared-mailbox accounts for now: deletion would need Mail.ReadWrite.Shared, and the own-mailbox client must never act on them.
  • --as without --graph is rejected; IMAP sync is unchanged.

Review boundaries

  • internal/msmail/client.go: mailbox root (/me or /users/<address>); DeltaStartURL becomes a method.
  • internal/microsoft/graph_oauth.go: GraphMailSharedScopes, NewGraphMailSharedManager, AuthorizeAs (Authorize delegates to it).
  • cmd/msgvault/cmd: --as flag, msmailSourceConfig, sync wiring, deletion guard.
  • Docs: OAuth setup guide (shared and delegated mailboxes), CLI reference, changelog.

Linear: TJC-3129

🤖 Generated with Claude Code

`add-o365 <mailbox> --graph --as <user>` signs in as the user, verifies the
ID token is that user's, requests Mail.Read.Shared, and saves the token under
the mailbox's address. The source records the user in its sync config, and
sync points the Graph mail client at /users/<mailbox> instead of /me, so the
same per-folder delta walk and $value MIME download read the shared mailbox.

delete-staged refuses these accounts for now: deletion would need
Mail.ReadWrite.Shared, and the own-mailbox client must not act on them.

TJC-3129

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@arcaputo3
arcaputo3 force-pushed the tjc-3129-msgvault-graph-shared-mailbox branch from fa70bb1 to b6d254d Compare October 5, 2026 19:00
@arcaputo3
arcaputo3 merged commit 7adc4d6 into main Oct 5, 2026
3 of 29 checks passed
@arcaputo3
arcaputo3 deleted the tjc-3129-msgvault-graph-shared-mailbox branch October 5, 2026 19:01
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