Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
6.6 KiB
6.6 KiB
Microsoft Graph Inbound Email Parity Plan
Status: In Progress Owner: Email Platform Last updated: 2025-08-16
Goals
- Achieve functional parity between Microsoft (Graph) and Gmail inbound email providers.
- Use consistent token storage, webhook setup/renewal, and message retrieval flows.
- Ensure webhook routes can publish enriched INBOUND_EMAIL_RECEIVED events reliably.
Current State (Summary)
- Gmail: Production-ready with OAuth lifecycle, Pub/Sub watch, DB persistence, webhook handler publishing enriched events.
- Microsoft: Basic OAuth refresh and webhook subscription exist; tokens stored in tenant secrets; limited DB persistence; webhook route publishes minimal events only; header/body fetch and connection checks are lighter than Gmail.
Scope of Work
- Credentials: Load + Persist in DB (not tenant secrets)
- Update
MicrosoftGraphAdapter.loadCredentials()to readaccess_token,refresh_token,token_expires_atfrommicrosoft_email_provider_configviaconfig.provider_config(matches Gmail pattern). - Update
MicrosoftGraphAdapter.refreshAccessToken()to persist refreshed tokens and expiry back tomicrosoft_email_provider_config(use admin/tenant DB access akin to Gmail adapter) instead ofemail_provider_credentialssecret. - Keep
client_id/client_secretlookup via env first, then tenant secrets as fallback.
- Webhook Subscription: Persist + Validate
- On subscription create/renew in
MicrosoftGraphAdapter:- Set
email_providers.webhook_id = subscription.id(route uses this for lookup). - Persist
webhook_subscription_idandwebhook_expires_atintomicrosoft_email_provider_config.
- Set
- Use
clientStatefor validation:- Source:
config.webhook_verification_token(reuse main provider field) to avoid schema changes. - Ensure the Microsoft webhook route validates
notification.clientStateagainstemail_providers.webhook_verification_token; keep backward compatibility by also acceptingprovider.provider_config.clientStateif present.
- Source:
- Webhook Route Behavior: Enriched events (parity with Gmail)
- In
app/api/email/webhooks/microsoft/route.ts, after provider lookup and validation:- Instantiate
MicrosoftGraphAdapterwith the resolved provider config. - Fetch full message details (
getMessageDetails(id)). - Publish
INBOUND_EMAIL_RECEIVEDwithemailDatapayload, mirroring Gmail flow.
- Instantiate
- Keep minimal event publish as fallback if fetch fails (warn and continue to ack webhook).
- Message Retrieval Parity
- Enhance
getMessageDetailsto request/select the needed fields:- Use
$select=internetMessageHeaders,receivedDateTime,subject,body,bodyPreview,from,toRecipients,ccRecipients,conversationIdand$expand=attachments. - Map
headers,references,inReplyTo,threadId, attachments metadata the same way Gmail does. - Consider
Prefer: outlook.body-content-type="text"for consistentbody.textextraction.
- Use
- Connection Test Parity
- Update
testConnection()to verify the mailbox:- Compare
/me(usemailoruserPrincipalName) toconfig.mailbox, returning a mismatch error like Gmail.
- Compare
- Processed Semantics (Optional but recommended)
- Current:
isRead=true. Improve by adding a category (e.g.,PSA/Processed) or moving to a folder, mirroring Gmail’s label approach.
Acceptance Criteria
- Tokens for Microsoft are loaded from and persisted to
microsoft_email_provider_config(DB), not tenant secrets. - Webhook subscription creation/renewal writes:
email_providers.webhook_idset to Graph subscription ID.microsoft_email_provider_config.webhook_subscription_idandwebhook_expires_atupdated.
- Webhook route validates
clientStateagainstemail_providers.webhook_verification_token(and optionally against historicalprovider_config.clientState). - Microsoft webhook route publishes enriched
INBOUND_EMAIL_RECEIVEDevents containingemailData(subject, body, headers, attachments, participants). testConnection()reports mailbox mismatch explicitly.- Gmail inbound flow remains unaffected.
Implementation Plan (Tasks)
- Adapter: Credentials
- Refactor credential load/refresh to use DB; add DB update helper mirroring Gmail’s
updateStoredCredentials().
- Refactor credential load/refresh to use DB; add DB update helper mirroring Gmail’s
- Adapter: Webhooks
- Create/renew watch writes to
email_providersandmicrosoft_email_provider_config; useconfig.webhook_verification_tokenasclientState.
- Create/renew watch writes to
- Adapter: Message Retrieval
- Expand
getMessageDetails()to include headers, correct body handling, and attachments using$select/$expandand Prefer text body.
- Expand
- Adapter: Connection Test
- Compare
/memailbox vsconfig.mailboxand report mismatch, usingmailoruserPrincipalName.
- Compare
- Route: Microsoft Webhook
- Lookup provider by
webhook_id, validateclientState, fetch details via adapter, publish enriched event. - Log + continue on per-message fetch failures (fallback minimal publish).
- Lookup provider by
- Route: Validation
- Update validation to prioritize
webhook_verification_token; maintain backward compatibility (warn if absent).
- Update validation to prioritize
Data/Schema Considerations
- No new columns required if we reuse
email_providers.webhook_idandemail_providers.webhook_verification_token. - Assumes
microsoft_email_provider_confighaswebhook_subscription_idandwebhook_expires_at(EmailProviderService mapping already supports these fields).
Testing Strategy
- Unit: Adapter methods for credential load/refresh (mock axios), message fetch with
$select/$expandmapping. - Integration: Webhook route end-to-end using mocked Graph API responses.
- E2E: Simulate Microsoft webhook payload and verify enriched
INBOUND_EMAIL_RECEIVEDpublished; verify DB persistence of subscription and tokens.
Rollout & Migration
- For existing Microsoft providers:
- Backfill
email_providers.webhook_idon next renewal or during a one-time maintenance script that reads current subscription and stores it. - First refresh post-deploy moves tokens into
microsoft_email_provider_config.
- Backfill
- Monitor webhook processing logs for clientState mismatches.
Risks & Mitigations
- Risk: Token source switch (secrets → DB) can desync.
- Mitigation: Keep a temporary fallback to secrets for reads; write always to DB.
- Risk: Graph throttling.
- Mitigation: Guard retries and log; backoff on message fetch.
- Risk: Different mailbox principal vs SMTP address.
- Mitigation: Use both
mailanduserPrincipalNamefor match.
- Mitigation: Use both
Timeline (Rough)
- Day 1–2: Adapter refactor (credentials, webhooks persistence, connection test).
- Day 2–3: Message retrieval enhancements; webhook route enrichment.
- Day 4: Tests and verification; rollout plan preparation.