[Microsoft Teams] Rewrite connector to use application-only permissions - #4257
[Microsoft Teams] Rewrite connector to use application-only permissions#4257Jan-Kazlouski-elastic wants to merge 26 commits into
Conversation
…ns (#4145) Replace the dual delegated + username/password auth model with application-only authentication (client secret or certificate), mirroring SharePoint Online. - Extract shared Microsoft Graph auth + HTTP layer into connectors/sources/shared/microsoft/graph.py, reused by SharePoint Online and Teams. - Adopt the least-privilege WhereInstalled/RSC permission model; per-resource message/member calls skip teams/chats where the app is not installed. - Trim synced entities to Teams, Channels (+ messages/replies/attachments), TeamMembers, and Chats (+ messages/attachments); drop calendars, tabs and meeting recordings. - Add Document Level Security based on team/chat membership, creating identities only for users that participate in Teams. - Ship a Teams app manifest template and setup docs for RSC installation. - Rewrite unit tests and the functional-test fixture accordingly. Co-authored-by: Cursor <cursoragent@cursor.com>
…lity Fix large-tenant get_docs stall via concurrent enumeration, refuse silent index wipe on total permission failure, index attachment-only messages, and surface skipped resources at end of sync. Co-authored-by: Cursor <cursoragent@cursor.com>
Make resource enumeration failure-safe: unexpected (non-permission) errors are recorded and re-raised from get_docs instead of stranding the consumer, with ENUMERATION_FINISHED always emitted in a finally. Process channels inline within team_producer to remove the nested fetchers scheduling that could deadlock the bounded task pool on large tenants, and always emit each producer's end signal via finally. Also add a return annotation to the recursive get_channel_drive_children to satisfy the type checker. Co-authored-by: Cursor <cursoragent@cursor.com>
…isks Private/shared channels now use channel membership for DLS (or are skipped when members cannot be resolved). Producer failures and one-sided or total message-access permission skips abort the sync instead of succeeding and deleting previously indexed content. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace install-scoped RSC/WhereInstalled access with tenant-wide Graph
application permissions and discover chats via team members'
GET /users/{id}/chats, matching the operable Outlook-style crawler + DLS model.
Co-authored-by: Cursor <cursoragent@cursor.com>
…sions Drop incomplete Graph $expand shortcuts for chat members and channel replies, discover chats from a single user-id pass, require Files.Read.All when attachments are enabled, and fail content/ACL syncs on PermissionsMissing. Co-authored-by: Cursor <cursoragent@cursor.com>
…iles Replace Team Member docs with User (Entra id), add member_ids on Team/Channel/Chat, and index drive items as File with message attachments[] links via contentUrl resolve. Co-authored-by: Cursor <cursoragent@cursor.com>
Set channel_* or chat_* on File only when known, and fetch team detail when list /teams omits webUrl or createdDateTime. Co-authored-by: Cursor <cursoragent@cursor.com>
…user:/user_id: ACL Hydrate User docs and DLS identities from GET /users (batched), keeping User content scoped to team members. Stamp mail as email: and UPN as user: (SPO-aligned) instead of treating UPN as a mailbox. Co-authored-by: Cursor <cursoragent@cursor.com>
The sync sink pops _id from the shared attachment dict before get_content runs; use item_id so File docs still index and extract. Co-authored-by: Cursor <cursoragent@cursor.com>
Restrict membership content DLS to user_id: tokens, omit ACLs on User docs, and store Graph UPN on User content while identity docs keep all dialects. Co-authored-by: Cursor <cursoragent@cursor.com>
Stop re-indexing on rediscovery from message attachments vs Files folder; merge parents/ACLs onto the in-flight doc so body is not wiped. Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror SharePoint Online so aiohttp no longer warns about an unclosed ClientSession when use_text_extraction_service is enabled. Co-authored-by: Cursor <cursoragent@cursor.com>
Do not fall back to conversationMember id, which is not an Entra oid and would stamp dead user_id: tokens that never match identity docs. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Calendar sync could still be nice, so not sure why this dropped it? It would require Calendar.Read Meeting recordings - are we just relying on Sharepoint if the user wants this? Otherwise, why drop them? Channel and call metadata, again might be valuable. |
|
According to #4145 Teams connector must focus on teams native target entities (teams, channels, chats, messages, members, attachments). For all the entities we've dropped compared to legacy connector - main reason is that they are not listed in the tracking issue. CalendarsOutlook connector seems to be a better fit for calendars today. Teams connector would be fetching only a subset of the same entities that are fetched by Outlook connector. Meeting recordingsThe legacy connector did not index recording binary/transcripts. It emitted lightweight metadata docs when a channel/chat system message carried Channel / call metadata (tabs, call-ended, etc.)Being a native MS Teams entities - these could be synced, though I am not sure if they are very useful. We could take it as a follow up, or include it here. |
…ipants Document Chat.Read.All as the sole chat app permission, and emit User docs for team, private/shared channel, and chat members (not team members only).
Drop private/shared phrasing from the required-permissions table and align related sync notes with membershipType-based channel member resolution.
Microsoft Teams connector — Elasticsearch document schemasGraph resources: team, channel, chat, chatMessage, conversationMember, user, driveItem. DLS: Skipped messages: deleted ( Team
Channel
Channel Message
ChatDiscovered via
Chat Message
UserContent document (directory metadata). No
FileWhen
Access Control (identity document)Emitted by
Message
|
| Elasticsearch field | Graph field | Summary |
|---|---|---|
attachments[].id |
driveItem.id |
File document _id (from shares resolution of attachments[].contentUrl). |
attachments[].title |
driveItem.name or attachments[].name |
Display title; falls back to Graph attachment name. |
Closes #4145
Rewrites the Microsoft Teams connector to application-only authentication (client secret or certificate), matching SharePoint Online / Outlook. Removes the legacy dual auth model (username/password + delegated Graph) that cannot work headlessly with MFA.
Auth and permissions
WhereInstalled)..Allapplication permissions, documented inmicrosoft_teams/README.md:Team.ReadBasic.All,TeamMember.Read.AllUser.ReadBasic.All(profile hydrate for User docs + DLS identityemail:/user:/user_id:)Channel.ReadBasic.All,ChannelMember.Read.All,ChannelMessage.Read.AllChat.ReadBasic.All,Chat.Read.AllFiles.Read.Allwhen "Fetch attachment content" is enabled (validated from the app tokenrolesclaim invalidate_config)GET /users/{id}/chats(deduped by chat id). There is no tenant-wideGET /usersor/chatssync.ChannelMessage.Read.All/Chat.Read.Allremain Microsoft protected Teams APIs; admin consent alone may not be enough until protected API access is granted for the app.Why not RSC / least-privilege
WhereInstalled(issue 3.1): RSC only scopes message reads after a Teams app is installed into each team/chat. There is no tenant-wide "list chats where installed" path for a crawler; install UX/Graph packaging failed in practice for an RSC-only (non-bot) package; requiring install-everywhere is not operable for enterprise search. Privacy for end users is enforced with DLS from membership (same model as SPO/Outlook), not by limiting what the crawler app can read.Shared Graph auth/HTTP (
connectors/sources/shared/microsoft/graph.py) is reused by SharePoint Online.Sync model
member_ids(Entra user ids). User docs includename,email(Graphmailonly),upn(GraphuserPrincipalName).referenceattachments resolved viacontentUrl→ shares API. Each driveItem id is queued once per sync (folder + message rediscovery merges parents/ACLs in memory; messages link viaattachments: [{id, title}]).GET /chats/{id}/members(not$expand=members, which Graph caps at 25).GET .../messages/{id}/replies(not incomplete$expand=replies)._allow_access_controlusesuser_id:only (EntrauserId, never conversationMemberid). Identity docs carryuser_id:+email:(mail) +user:(UPN). User content docs omit_allow_access_control(directory metadata).PermissionsMissing(401/403) fails content sync and ACL sync.NotFound(404) may soft-skip absent resources and is summarized at end of sync.Upgrade / corpus change (callout)
On first successful full sync after upgrade, documents that this rewrite no longer produces are deleted from the content index if they were left by the previous connector, including: calendars, channel/chat tabs, meeting recordings, and legacy shapes such as Team Member (replaced by User). Operators should expect that cleanup. Synced going forward: teams, channels, channel messages/replies, chats of team members, chat messages, Users (team members), Files (when enabled), and DLS identities.
Tests
user_id:-only content ACL; identity dialects), File single-queue / deferred_id, extraction session close, membership-idACL ignore./users/{id}/chats,/chats/{id}/members, message replies, and user profile routes.Checklists
Pre-Review Checklist
config.yml.example)v7.13.2,v7.14.0,v8.0.0)Changes Requiring Extra Attention
Related Pull Requests
member_ids,upn, Files).Release Note
Rewrote the Microsoft Teams connector for application-only authentication (client secret or certificate) using tenant-wide Graph application permissions and document-level security, replacing username/password + delegated auth. Syncs Teams, Channels, Chats (of team members), Users, messages/replies, and Files; content DLS uses
user_id:membership tokens with identity docs for email/UPN login. Upgrading removes previously indexed calendars, tabs, meeting recordings, and legacy Team Member documents on the next successful full sync.