Graph work/school: request explicit mail scopes at add-time so consent prompts (#391) - #392
Graph work/school: request explicit mail scopes at add-time so consent prompts (#391)#392CityDweller wants to merge 2 commits into
Conversation
#391) `.default` (used for work/school Graph mail) returns a token for what's already granted and doesn't drive an interactive consent prompt, so adding an account on a tenant that hasn't consented completes with no prompt and every mail call then 403s (Authorization_RequestDenied). Personal accounts already use explicit scopes for this same reason. Request explicit mail scopes at FIRST connect (GraphMailScopesWork: Mail.ReadWrite, Mail.Send, User.Read) via new FirstConnectScopesFor, so adding a work/school account prompts for consent. Silent refresh still uses `.default` (DefaultScopesFor unchanged), preserving the requested-vs-declared match from #208. MailboxSettings.ReadWrite (admin-restricted, server rules) is not requested at add-time so ordinary users aren't blocked; `.default` picks it up after admin consent. Tests: work/school first-connect uses the explicit set (not `.default`), personal uses the personal set, IMAP uses IMAP scopes, and refresh still uses `.default`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The first-connect consent was too narrow (Mail.ReadWrite/Send/User.Read only), so it missed permissions QuickMail needs. Expand GraphMailScopesWork to the full set of delegated Graph scopes the app uses (ENTRA-APP-REGISTRATION.md §3) — MailboxSettings.ReadWrite (server rules), Calendars.ReadWrite, Contacts.Read, People.Read, User.ReadBasic.All — matching the coverage `.default` gave but with a working prompt, so one add-time consent covers everything. Contacts.ReadWrite stays out (forward-declaration, unused). All scopes are declared, so no requested-vs-declared re-prompt loop (#208); refresh still uses `.default`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Context first: #393 (account setup) reworks how accounts are created, and one change there makes this PR considerably more load-bearing than it looks today. Work-or-school Microsoft accounts on a custom domain now default to the Graph backend, because the IMAP path requests That means this is worth getting exactly right, and I would like to work through it with you rather than merge it as-is. Findings below, roughly by severity. Some of these I am confident about; where I am not, I have said so. 1. The description and the diff disagree — this one is definiteThe body and #391 both say:
The diff ships eight scopes, including I think the code is the better of the two, so the fix is to correct the description rather than the array. Which matters, because the description's plan would break server rules: it says 2. The root-cause claim does not match Microsoft's docsThe stated cause is that The documented gap is Example 3 instead: once any grant exists, If that is the actual mechanism, the documented remedy is Two things would settle it: does the failing tenant's log show the 403 on a mail call, or on a 3. Sharpest concrete risk: personal MSA on a vanity domain
Those accounts would get #393 makes this reachable, since it routes any Microsoft address on a non-consumer domain to Graph. I handled the mirror image of this problem there by correcting after sign-in, when the tenant id is known. Simplest fix here is probably to not select the work set when 4. Stale-grant tenants may go from degraded to blockedA tenant whose admin consented before 5. One interactive path still uses
|
Fixes #391.
Adding a work/school M365 account on a tenant that hasn't consented to QuickMail completes sign-in with no consent prompt, then every Graph mail call 403s (
Authorization_RequestDenied). Reported by a tenant admin adding a first account on a brand-new tenant.Cause: work/school mail requests only
.default, which returns a token for what's already granted and doesn't drive an interactive consent prompt on a fresh tenant — so it silently yields a partial token. (Personal accounts already use explicit scopes for exactly this reason; contacts/calendar use explicit scopes and do prompt, which is why those worked and mail didn't.)Fix: request explicit mail scopes at first connect for work/school (
GraphMailScopesWork=Mail.ReadWrite,Mail.Send,User.Read) via newFirstConnectScopesFor, so adding the account prompts for consent — mirroring the personal-account path. Silent refreshes still use.default(DefaultScopesForunchanged), so the requested-vs-declared match from #208 is preserved.MailboxSettings.ReadWrite(admin-restricted, server rules) is intentionally not requested at add-time so ordinary users aren't blocked by an admin-approval wall;.defaultpicks it up after admin consent.Tests: work/school first-connect returns the explicit set (not
.default), personal returns the personal set, IMAP returns IMAP scopes, and refresh still uses.default. Full scope-selection suite green.Small and self-contained; no change to the refresh path or the #208 fix.