Microsoft Teams connector setup

Operator·4 minutes to read

This page is the setup half: registering the app Microsoft Teams needs in the Microsoft Entra admin center and getting its client id and secret into this deployment. A member then adds Microsoft Teams from the catalogue and presses Authenticate, which signs them in with their work or school account and asks for reading only. Sending is a second consent the member asks for later with Allow sending on the same account.

Microsoft Teams is its own plugin, with its own redirect URL saying microsoft-teams. It is not part of the microsoft-365 plugin, so a member who connected Microsoft 365 keeps that connection unchanged.

1. Register the app in the Microsoft Entra admin center

  1. Open the Microsoft Entra admin center and register a new application under Identity → Applications → App registrations. Under Supported account types, choose Accounts in any organizational directory: Teams is for work and school accounts, and a personal Microsoft account has no Teams data to read.

  2. Under Redirect URIs (platform Web), add the redirect URL from docs/connectors/README.md. With the quickstart's default BOTSEON_PUBLIC_URL, that is:

    http://127.0.0.1:3000/api/oauth/microsoft-teams/callback
    
  3. Under Certificates & secrets, create a new client secret. Copy the Application (client) ID from Overview and the secret's value, which is shown once.

2. Scopes

All are Microsoft Graph delegated permissions: the bot acts as the member and sees only what the member can see. The first sign-in asks for the read half:

  • User.Read: the member's own profile, to label the connection and to tell their own messages from others'.
  • offline_access: a refresh token, so the connection outlives the hour-long access token.
  • Team.ReadBasic.All: the teams the member is in.
  • Channel.ReadBasic.All: the channels of those teams.
  • ChannelMessage.Read.All: channel posts and their replies.
  • Chat.Read: the member's one-to-one and group chats, and who is in each.
  • TeamMember.Read.All: who is in a team the member is in (list_members).
  • ChannelMember.Read.All: who is in a channel the member is in, a private channel's own people included (list_members).

Allow sending asks for the send half on top:

  • ChannelMessage.Send: post to a channel and reply in a thread.
  • ChatMessage.Send: send a chat message.
  • ChannelMessage.ReadWrite, Chat.ReadWrite, Presence.ReadWrite: react, delete the account's own message and set its status (S-96818). Chat.ReadWrite also starts a new chat (create_chat); Graph lists it above Chat.Create for that call, so starting a chat asks for nothing more.

Starting a chat waits on an approval card that lists the exact emails the chat is with; the connector binds the chat's members to those addresses and nothing else, so there is no name to check back.

Every send waits on an approval card that names the channel ("Team / Channel") or the chat. Before posting, the connector reads that channel's or chat's own name back from Microsoft and sends nothing if it is not the one on the card.

Admin consent. Microsoft marks ChannelMessage.Read.All, TeamMember.Read.All and ChannelMember.Read.All as needing an administrator's consent, and many organisations turn off user consent for every app. In either case the member's sign-in stops at Microsoft with "Need admin approval" until a Global Administrator, Application Administrator or Cloud Application Administrator of the member's own organisation grants consent once for the app (Enterprise applications → the app → Permissions → Grant admin consent). That is Microsoft's rule for the member's tenant. Botseon adds no approval step of its own.

3. Enter the app

Pass the client secret on standard input. No flag takes it and no environment variable holds it (see docs/connectors/README.md):

printf %s "$CLIENT_SECRET" | botseon oauth-app set \
  --org <organisation id> --slug microsoft-teams \
  --client-id <the client ID from step 1> \
  --auth-url https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize \
  --token-url https://login.microsoftonline.com/organizations/oauth2/v2.0/token \
  --configured-by <your user id>

organizations (not common) is the authority that accepts work and school accounts only. No --revoke-url is set, because Microsoft has no endpoint an app can call to revoke a member's delegated grant (see §4).

An existing organisation sees the new tile after an owner or admin accepts the new catalogue seed hash with botseon catalogue import --org <organisation id> --accept --accepted-by <their user id>.

4. Disconnecting

Remove on the account deletes the member's tokens here and marks the account revoked. The bot loses access at once: the next tool call finds no connection and nothing reaches Microsoft.

Microsoft offers no call an app can make to withdraw a member's consent from their side. revokeSignInSessions would sign the member out of every application, and deleting the consent grant needs an administrator's permission. So the consent record stays in the member's Microsoft account until they remove it at My Apps (Manage your applications), or until their administrator does. The refresh token this deployment held is deleted either way, and no access token outlives its one-hour lifetime.

Last verified against build eb35b70.