feat(microsoft-shared): Phase 1 — OAuth + send via Graph API

Adds a new "microsoft_shared" Email Channel provider that uses Microsoft
Graph API end-to-end for sending. Bypasses xoauth2 SMTP, so it works on
tenants with Security Defaults enabled and survives the April 2026 SMTP
Basic Auth retirement.

Flow:
1. Admin picks "Microsoft Shared Inbox" in the Add Inbox wizard
2. Enters the shared mailbox UPN (e.g. sales@company.com)
3. Single OAuth round-trip as a delegate user; UPN is carried in a signed
   state parameter so the callback knows which mailbox to bind
4. Callback verifies the OAuth user has access to the UPN via
   GET /users/{upn}/mailFolders/inbox before creating the channel
5. Replies route through a custom :microsoft_shared ActionMailer delivery
   method that POSTs base64-encoded MIME to /users/{upn}/sendMail

Channel modifications use the custom/ overlay (Custom::Leadchat::*).
Direct OSS edits are wrapped in '# === LeadChat: microsoft_shared ===' markers.

Phase 2 (Graph webhook receive) and Phase 3 (re-auth, subscription
failure surfacing, channel-destroy cleanup) follow.

Azure app prerequisites (one-time, manual): add delegated permissions
Mail.Send.Shared, Mail.ReadWrite.Shared, User.Read, offline_access; grant
admin consent. No new app registration required.

See LEADCHAT.md for the full customization index.
This commit is contained in:
netlas
2026-04-27 12:17:01 +03:00
parent 800f2b2654
commit 78cfd07009
14 changed files with 413 additions and 0 deletions

View File

@@ -17,6 +17,7 @@ LeadChat is a white-label Chatwoot fork maintained on the `leadchat` branch. Cus
| Hook in OSS file | Overlay file | Adds |
|---|---|---|
| `app/models/channel/email.rb` (bottom) | `custom/app/models/custom/leadchat/channel/email_extension.rb` | `Channel::Email#microsoft_shared?` predicate |
| `app/mailers/conversation_reply_mailer_helper.rb` (bottom) | `custom/app/mailers/custom/leadchat/conversation_reply_mailer_helper_extension.rb` | Routes `microsoft_shared` channels to the `:microsoft_shared` ActionMailer delivery method (Graph API), bypassing xoauth2 SMTP |
### B. Direct edits to OSS files
@@ -24,6 +25,10 @@ LeadChat is a white-label Chatwoot fork maintained on the `leadchat` branch. Cus
|---|---|---|
| `config/application.rb` | Mirror enterprise/ autoload setup for `custom/` so `Custom::*` modules are autoloaded and `custom/config/initializers/**/*.rb` are required | `# === LeadChat: custom/ overlay autoload ===` |
| `app/models/channel/email.rb` (bottom) | One-line `include_mod_with` hook for the email_extension overlay | `# === LeadChat: microsoft_shared ===` |
| `app/mailers/conversation_reply_mailer_helper.rb` (bottom) | One-line `prepend_mod_with` hook for the helper extension overlay | `# === LeadChat: microsoft_shared ===` |
| `config/routes.rb` (3 separate blocks) | (1) `app_new_microsoft_shared_inbox` dashboard route; (2) account-namespaced `microsoft_shared/authorization` API endpoint; (3) `microsoft_shared/callback` OAuth callback | `# === LeadChat: microsoft_shared ===` |
| `app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Email.vue` (3 separate blocks) | (1) import `MicrosoftShared.vue`; (2) `emailProviderList` entry for `microsoft_shared`; (3) router slot `<MicrosoftShared v-else-if="provider === 'microsoft_shared'"/>` | `<!-- === LeadChat: microsoft_shared === -->` and `// === LeadChat: microsoft_shared ===` |
| `app/javascript/dashboard/i18n/locale/en/inboxMgmt.json` | New keys: `INBOX_MGMT.ADD.MICROSOFT_SHARED.*` and `INBOX_MGMT.EMAIL_PROVIDERS.MICROSOFT_SHARED.*` | (no comment markers — JSON; flagged here in LEADCHAT.md instead) |
### C. New top-level files (no upstream conflict potential)
@@ -31,6 +36,14 @@ LeadChat is a white-label Chatwoot fork maintained on the `leadchat` branch. Cus
|---|---|
| `LEADCHAT.md` | This file |
| `custom/` | Root of LeadChat overlay tree, picked up by `ChatwootApp.custom?` and the Phase-0 autoload setup |
| `custom/config/initializers/microsoft_shared_delivery.rb` | Registers `:microsoft_shared` ActionMailer delivery method |
| `app/controllers/concerns/microsoft_shared_concern.rb` | OAuth client + Graph scopes + signed-state encoding/decoding for the new provider |
| `app/controllers/api/v1/accounts/microsoft_shared/authorizations_controller.rb` | Accepts shared mailbox UPN, returns Microsoft authorize URL with UPN embedded in signed state |
| `app/controllers/microsoft_shared/callbacks_controller.rb` | OAuth callback: decodes state, exchanges code, verifies UPN access via Graph, creates `Channel::Email` + `Inbox` |
| `app/services/microsoft/shared/send_mail_service.rb` | Builds RFC 822 MIME and POSTs base64-encoded to `/users/{upn}/sendMail` via Graph |
| `app/mailers/microsoft_shared_delivery.rb` | Custom ActionMailer delivery method that delegates to the send service |
| `app/javascript/dashboard/api/channel/microsoftSharedClient.js` | Frontend API client |
| `app/javascript/dashboard/routes/dashboard/settings/inbox/channels/emailChannels/MicrosoftShared.vue` | Single-step UPN entry form before the OAuth redirect |
### D. Brand script targets