Teams MCP - Security — Unique AI Documentation

Teams MCP - Security

This document describes the security architecture, cryptographic decisions, and threat model for the Teams MCP Server.

Security Layers

Token Security

Microsoft Tokens (Encrypted at Rest)

Microsoft access and refresh tokens are stored encrypted using AES-256-GCM:

Aspect Implementation
Algorithm AES-256-GCM (authenticated encryption)
Key Size 256 bits (64 hex characters)
IV Random 12 bytes per encryption
Authentication Built-in with GCM mode
Key Storage ENCRYPTION_KEY environment variable

Why AES-GCM:

Token Lifecycle:

  1. Microsoft issues tokens during OAuth flow
  2. Tokens encrypted immediately before database write
  3. Tokens decrypted only when needed for Graph API calls
  4. Tokens re-encrypted after refresh

MCP Tokens (Hashed for Validation)

MCP access and refresh tokens use a different approach:

Token Type Storage Validation
Access Token SHA-256 hash Cache-first, then database lookup
Refresh Token SHA-256 hash Database lookup with family check

Why Hashing (not Encryption):

OAuth 2.1 with PKCE

The MCP OAuth implementation follows OAuth 2.1 with mandatory PKCE. This diagram shows the MCP OAuth flow where the server issues opaque JWT tokens to the client after the Microsoft OAuth flow completes on the server.

Important: The tokens issued to the client in this flow are opaque JWTs for MCP authentication, not Microsoft tokens. Microsoft tokens remain on the server and are never sent to clients.

PKCE Protection:

Token Separation:

References:

Refresh Token Rotation

Refresh tokens are rotated on every use with family-based revocation:

Token Family Revocation: Each OAuth session has a token_family identifier. When a refresh token is used:

  1. Server checks if token was already used
  2. If reused → entire family revoked (possible theft)
  3. If valid → token marked used, new token issued with same family

This detects scenarios where an attacker obtains a refresh token and uses it, but the legitimate client also tries to use the original.

Webhook Validation

Microsoft Graph webhooks are validated using clientState:

Validation Details:

Write Surface (Send Tools)

The send_chat_message and send_channel_message tools are write operations — they post messages to Teams chats and channels as the signed-in user. This is the first write capability in the Teams MCP Server and is worth stating explicitly.

These tools operate under the same delegated-OAuth and token-isolation model as all read operations: Microsoft tokens are stored encrypted on the server and never sent to the client, and the agent can only post to chats and channels that the signed-in user can access. The ChannelMessage.Send and ChatMessage.Send permissions cover sending only; they do not grant read access to message content.

Channel Message Admin Consent

Reading channel message content (get_channel_messages, search_messages with detail=full) requires the ChannelMessage.Read.All permission, which requires admin consent because channel messages may contain sensitive organisational content. Without admin consent for this permission, the send and list tools remain fully functional.

See Permissions for the full consent breakdown and the distinction between user-consentable and admin-consent-required scopes.

Secret Management

Required Secrets

Secret Purpose Rotation Impact
ENCRYPTION_KEY Encrypt Microsoft tokens All users must reconnect
AUTH_HMAC_SECRET Sign MCP JWTs All sessions invalidated
MICROSOFT_CLIENT_SECRET Authenticate with Entra ID Update and restart
MICROSOFT_WEBHOOK_SECRET Validate webhooks Recreate all subscriptions

Rotation Procedures

ENCRYPTION_KEY Rotation:

  1. There is no zero-downtime rotation—key change invalidates all stored tokens
  2. Deploy with new key
  3. All users must reconnect to MCP server
  4. Consider warning users before rotation

AUTH_HMAC_SECRET Rotation:

  1. Change secret and deploy
  2. All MCP sessions immediately invalidated
  3. Clients will re-authenticate automatically

MICROSOFT_CLIENT_SECRET Rotation:

  1. Create new secret in Entra ID
  2. Update Kubernetes secret
  3. Restart pods
  4. Delete old secret from Entra ID

MICROSOFT_WEBHOOK_SECRET Rotation: Currently not possible - There is no easy way to invalidate all existing subscriptions that were created with the old secret. Existing subscriptions have the old secret as clientState and will fail validation if the secret changes, but there's no automated mechanism to recreate all subscriptions.

Note: Automated rotation might be part of a future release.

If rotation becomes necessary, it would require:

  1. Generating new 128-character secret: openssl rand -hex 64
  2. Manually deleting all subscriptions via Microsoft Graph API
  3. Having all users reconnect to MCP server to trigger subscription recreation
  4. Updating Kubernetes secret and deploying

Important: Recreation may miss transcripts created during the gap between deletion and recreation.

Security Checklist for Operators

  1. ENCRYPTION_KEY is a cryptographically random 64-character hex string
  2. AUTH_HMAC_SECRET is a cryptographically random 64-character hex string
  3. MICROSOFT_WEBHOOK_SECRET is a cryptographically random 128-character string
  4. All secrets stored in Kubernetes secrets (not ConfigMaps)
  5. Network policies restrict pod-to-pod communication
  6. TLS termination configured at ingress
  7. Log aggregation excludes sensitive fields (tokens are auto-redacted)
  8. Monitoring alerts configured for authentication failures

Standard References