SPEC 017 — Microsoft Integration
| Field | Value |
|---|---|
| Status | DRAFT |
| Priority | P2 — Integration |
| Backend | equa-server/modules/microsoft/ |
| Frontend | equa-web/src/shared/helpers/ms/msGraph.ts |
1. Feature Purpose
The Microsoft Integration connects Equa organizations to Microsoft 365 via Azure AD. It uses the OAuth 2.0 client credentials flow to obtain application-level access to the Microsoft Graph API, enabling the platform to manage Microsoft 365 groups on behalf of organizations. This powers team provisioning, group membership synchronization, and future SharePoint/Teams integrations without requiring individual user consent.2. Current State (Verified)
2.1 OAuth Client Credentials Flow
| Detail | Value |
|---|---|
| Grant type | client_credentials (application-level, no user interaction) |
| Authority | https://login.microsoftonline.com/{MS_AUTH_TENANT_ID} |
| Scope | https://graph.microsoft.com/.default |
| Library | @azure/msal-node ConfidentialClientApplication |
| Env vars | MS_AUTH_CLIENT_ID, MS_AUTH_TENANT_ID, MS_AUTH_SECRET, MS_AUTH_ENABLED |
2.2 Graph API Helper
| Detail | Value |
|---|---|
| File | equa-web/src/shared/helpers/ms/msGraph.ts |
| Client | @microsoft/microsoft-graph-client via Client.init() |
| Auth provider | Passes access token from MSAL into the Graph client auth callback |
2.3 Microsoft Group Binding
| Detail | Value |
|---|---|
| Storage | Organizations.microsoftGroup (uuid field on existing Organizations entity) |
| Lifecycle | Group ID stored when an organization links to an existing M365 group or creates a new one |
| Usage | Used to resolve group members, sync membership, and manage group settings via Graph API |
2.4 Backend Module
| Component | Path |
|---|---|
| Module root | equa-server/modules/microsoft/ |
| Endpoints | equa-server/modules/api/src/endpoints/microsoft-endpoints.ts |
3. Data Model
The Microsoft integration does not introduce dedicated entities. It relies on a single field on the existingOrganizations table and the Azure AD tenant for all state.
Organizations (relevant field)
| Column | Type | Constraints |
|---|---|---|
| microsoftGroup | uuid | nullable — Azure AD group object ID |
Token Caching
MSAL’sConfidentialClientApplication handles in-memory token caching and automatic refresh. No database persistence is required for tokens since the client credentials flow can always obtain a new token from Azure AD without user interaction.
4. API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/organizations/:id/microsoft/status | Yes | Check if MS integration is enabled and group is linked |
| POST | /api/v1/organizations/:id/microsoft/link-group | Yes | Associate an existing M365 group with the organization |
| POST | /api/v1/organizations/:id/microsoft/create-group | Yes | Create a new M365 group and link it |
| DELETE | /api/v1/organizations/:id/microsoft/unlink-group | Yes | Remove the M365 group association |
| GET | /api/v1/organizations/:id/microsoft/group | Yes | Fetch linked group details from Graph API |
| GET | /api/v1/organizations/:id/microsoft/group/members | Yes | List members of the linked M365 group |
| POST | /api/v1/organizations/:id/microsoft/group/members | Yes | Add a member to the linked M365 group |
| DELETE | /api/v1/organizations/:id/microsoft/group/members/:memberId | Yes | Remove a member from the linked M365 group |
| POST | /api/v1/organizations/:id/microsoft/sync-members | Yes | Sync Equa team members to/from M365 group membership |
5. Frontend Components
| Component | Path | Description |
|---|---|---|
| msGraph helper | equa-web/src/shared/helpers/ms/msGraph.ts | Initializes Microsoft Graph client with token auth provider |
| Settings integration section | equa-web/src/modules/settings/ | Organization settings page includes Microsoft group linking UI |
Frontend Behavior
- Feature gate — Microsoft integration UI elements are hidden when
MS_AUTH_ENABLEDis false. - No user OAuth — Since the integration uses client credentials, there is no user-facing OAuth popup. Admins link groups by entering or selecting a group ID.
- Graph client — The frontend helper (
msGraph.ts) creates a Graph client instance; however, in practice most Graph API calls are proxied through the backend to avoid exposing the application secret. - Group status display — Shows linked group name, member count, and last sync time when a group is associated.
6. Business Rules
- Application-level auth — The client credentials flow grants tenant-wide access; no individual user consent is required. This requires Azure AD admin consent for the app registration.
- One group per organization — Each Equa organization links to at most one M365 group via
Organizations.microsoftGroup. - Feature flag gating — All Microsoft endpoints return 404 when
MS_AUTH_ENABLEDis false, preventing accidental exposure. - Token lifecycle — MSAL handles token acquisition, caching, and refresh internally. Tokens are never persisted to disk or database.
- Tenant scoping — The integration operates within a single Azure AD tenant defined by
MS_AUTH_TENANT_ID. Multi-tenant support is not implemented. - Group creation defaults — Newly created M365 groups are configured as private with mail-enabled security group type.
- Member sync direction — Sync is bidirectional: Equa team members are added to the M365 group, and M365 group members without Equa accounts are flagged for invitation.
- Unlink preserves group — Unlinking an M365 group from an Equa organization clears
microsoftGroupbut does not delete the group in Azure AD. - Admin-only operations — Group linking, unlinking, creation, and member management require organization admin role.
- Graph API error handling — 401/403 from Graph API triggers a token refresh and single retry; persistent failures are surfaced to the user.
7. Acceptance Criteria
- Admin can link an existing M365 group to the organization by group ID
- Admin can create a new M365 group from the Equa settings page
- Admin can unlink the M365 group; the Azure AD group is not deleted
- Linked group details (name, description, member count) display correctly
- Group members list shows current M365 group membership
- Admin can add a member to the M365 group via the Equa UI
- Admin can remove a member from the M365 group via the Equa UI
- Member sync reconciles Equa team members with M365 group membership
- All Microsoft endpoints return 404 when
MS_AUTH_ENABLEDis false - Token refresh is automatic and transparent to the user
- Non-admin users cannot access Microsoft integration endpoints
- Graph API errors are surfaced with actionable messages
8. Risks
| Risk | Impact | Mitigation |
|---|---|---|
| Tenant-wide application permissions | Over-privileged access to all tenant resources | Request minimum Graph API permissions (Group.ReadWrite.All only); audit app registration scopes |
MS_AUTH_SECRET exposure | Full tenant access compromise | Store in vault; rotate on schedule; never log or return in API responses |
| Single-tenant limitation | Cannot support organizations in different Azure AD tenants | Document limitation; plan multi-tenant app registration if demand arises |
| Azure AD admin consent required | Onboarding friction for new tenants | Provide clear setup documentation; consider admin consent URL generator |
| In-memory token cache lost on restart | Minor latency spike on first post-restart call | Acceptable trade-off; MSAL re-acquires tokens automatically |
| Graph API rate limits (10 000 requests / 10 minutes per app) | Sync failures for large groups | Implement batching and throttle-aware retry with Retry-After header |
| M365 group deleted externally | Stale microsoftGroup reference, API errors | Health check on group status; clear stale references on 404 |
| Member sync conflicts (user exists in M365 but not in Equa) | Incomplete sync, orphaned members | Flag unmatched members for admin review rather than auto-creating accounts |