ADR 0011: Microsoft Graph OAuth and adapter
Status
Accepted
Context
Sprint 03 needs the first write-capable mailbox. Microsoft Graph is the documented first provider. Tokens must never reach the browser. SDK types must not leak out of the providers package.
Decision
- Use delegated OAuth 2.0 with PKCE against
login.microsoftonline.com/commonso consumer and M365 accounts can connect. - Scopes are
openid offline_access User.Read Mail.Read Mail.ReadWrite.Mail.Sendis rejected. - Persist access and refresh tokens only as AES-256-GCM ciphertext in
provider_tokens.connected_accounts.auth_secret_refis a pointer, not a secret. - Store
authorization_status(HEALTHY/AUTH_REQUIRED/DEGRADED) so health queries do not decrypt tokens. - Coordinate refresh with an in-process single-flight map plus a row lock so concurrent workers do not stampede the token endpoint.
- Implement the adapter against the existing
ProviderAdapterport with an injectable HTTP transport. Recorded Graph fixtures back CI. No live mailbox is required.
Alternatives
- Official Graph SDK as a public export — rejected; types stay inside
packages/providers/src/graph. - Per-user IdP login for Duckbill itself — already rejected by ADR 0005. This flow is mailbox authorization, not application login.
Consequences
apps/web calls application handlers only. Preview and production stay 503 on the OAuth routes until MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, MICROSOFT_REDIRECT_URI, and the existing database/token secrets are set per environment.
