Outlook Semantic MCP - Security
7 min read
Outlook Semantic MCP - Security
This document describes the security architecture, cryptographic decisions, and threat model for the Outlook Semantic MCP Server.
Security at a Glance:
No email content stored in the MCP server's database — it acts as a secure pass-through to the Unique knowledge base
Microsoft OAuth tokens encrypted at rest with AES-256-GCM
OAuth 2.1 with mandatory PKCE for all MCP client authentication
MCP tokens are 512-bit cryptographically random opaque values with TTL-based expiration
Refresh token rotation with family-based revocation detects token theft
Webhook notifications validated via
clientStatesecret
Data Classification and Flow
What Is Stored Where
The Outlook Semantic MCP Server stores no email content in its own database. It acts as a secure pass-through: emails are fetched from Microsoft Graph into memory and uploaded directly to the Unique knowledge base. Nothing from the email body, subject, or attachments is written to the MCP server's PostgreSQL database.
Data | Stored In | Protection | Contains Email Content? |
|---|---|---|---|
Microsoft OAuth tokens (access + refresh) | PostgreSQL | AES-256-GCM encrypted | No |
MCP bearer tokens | PostgreSQL | Opaque 512-bit random values | No |
Sync state and progress counters | PostgreSQL | Plaintext metadata | No |
Outlook folder structure (names + IDs) | PostgreSQL | Plaintext metadata | No |
Microsoft Graph subscription IDs | PostgreSQL | Plaintext metadata | No |
Email subject, body, sender, recipients | Unique Knowledge Base | Ingested for semantic search | Yes |
Data Flow
Connection and Sync — The user authenticates once. The server obtains Microsoft tokens (kept server-side, encrypted), issues an opaque MCP token to the client, and begins syncing emails into the Unique knowledge base. No email content is stored in the MCP server's database.

Tool Usage — The MCP client uses its opaque token to call tools. The server handles all Microsoft Graph interaction internally — the client never sees Microsoft tokens.

Data Removal
Action | What Is Removed |
|---|---|
User calls | Microsoft Graph subscription, per-user root scopes and all ingested email content in Unique KB, inbox configuration, folder sync data |
MCP tokens | Expire naturally (access: 60 seconds, refresh: 30 days); not automatically removed from KB |
When a user calls delete_inbox_data, the per-user root scopes are removed from the Unique knowledge base, which also removes all ingested email content for that user.
Knowledge Base Data Isolation
Each connected user's emails are stored in a dedicated root scope (a top-level isolation boundary in the Unique knowledge base that logically separates one user's data from another's) within the Unique knowledge base. Scopes are created automatically when the user connects (by DirectoriesSyncModule) and are removed when delete_inbox_data is called.
Logical isolation:
All emails from a connected mailbox are stored under that user's root scope
search_emailsonly queries the scope belonging to the authenticated user — the MCP server prevents one user's session from searching another user's emailsScope boundaries are enforced by the Unique scope management service
Unique platform-level access:
Access to email data at the Unique platform layer is governed by Unique's own access control model, not by the MCP server. Relevant access principals are:
Principal | Access Level |
|---|---|
Authenticated MCP user | Can search their own emails via |
MCP server service account | Write access to ingestion and scope management APIs (used during sync) |
Unique platform administrators | Can access email scopes via the Unique API (e.g. |
Direct PostgreSQL access (MCP DB) | Encrypted tokens and sync state only — no email content |
Email scopes created by the MCP server are not visible in the Unique Knowledge Base UI. Accessing them requires direct Unique API calls or database access. Organizations with strict email privacy requirements should control who has API and database access to the Unique platform.
Security Layers
Webhook Requests
Webhook requests pass through each layer from top to bottom. Each layer must pass before the next is reached.
Layer | Mechanism | Protects Against |
|---|---|---|
Transport | TLS 1.2+ via Kong Gateway | Eavesdropping, man-in-the-middle |
Rate Limiting | IP-based throttling at ingress layer (e.g., Kong) | Brute-force, abuse |
Webhook Integrity |
| Forged webhook notifications |
MCP Requests
MCP requests pass through each layer from top to bottom. Each layer must pass before the next is reached.
Layer | Mechanism | Protects Against |
|---|---|---|
Transport | TLS 1.2+ via Kong Gateway | Eavesdropping, man-in-the-middle |
Rate Limiting | IP-based throttling at ingress layer (e.g., Kong) | Brute-force, abuse |
Authentication | OAuth 2.1 + PKCE | Unauthorized access, authorization code interception |
Session Integrity | HMAC-SHA256 on OAuth session state | Session hijacking via forged callbacks |
Token Design | 512-bit cryptographically random opaque values | Token guessing |
Data at Rest | AES-256-GCM encryption for Microsoft tokens | Token theft from database |
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 (32 bytes, provided as 64 hex characters) |
IV | Random 12 bytes generated per encryption operation |
Stored Format |
|
Key Storage |
|
Why AES-GCM:
Provides both confidentiality and integrity — ciphertext tampering is detected before decryption
Industry standard for symmetric token encryption
GCM authentication tag prevents oracle attacks
Token lifecycle:
Microsoft issues tokens during OAuth flow
Tokens encrypted immediately before database write into
user_profilesTokens decrypted only when needed for Graph API calls
Re-encrypted after a successful refresh
MCP Tokens (Opaque Random Values)
MCP access and refresh tokens are cryptographically random opaque values:
Aspect | Implementation |
|---|---|
Generation |
|
Storage | Stored directly in |
Validation | Cache-first lookup, then database check |
Security property | Unguessability (512-bit random) — not hashing |
Token TTLs:
Token Type | Default TTL | Source | Configurable Via |
|---|---|---|---|
MCP Access Token | 60 seconds | Service limit |
|
MCP Refresh Token | 30 days | Service limit |
|
Microsoft Access Token | ~1 hour | Microsoft limit | Not configurable — issued by Microsoft |
Microsoft Refresh Token | ~90 days | Microsoft limit | Not configurable — issued by Microsoft |
Session State Integrity (AUTH_HMAC_SECRET)
During the OAuth callback, the server validates session state using HMAC-SHA256:
HMAC-SHA256(AUTH_HMAC_SECRET, "{sessionId}:{sessionNonce}") → base64urlThis prevents an attacker from injecting a forged OAuth callback that could hijack another user's session.
OAuth 2.1 with PKCE
The MCP OAuth implementation follows OAuth 2.1 with mandatory PKCE. The tokens issued to the client are opaque MCP tokens — Microsoft tokens remain on the server and are never sent to any client.

PKCE protection:
Prevents authorization code interception attacks
Mandatory for all clients
S256method:SHA256(code_verifier)encoded as base64urlAuthorization codes are single-use — consumed immediately after exchange
Token separation:
Token | Stays On | Used For |
|---|---|---|
Microsoft access token | Server only | Graph API calls |
Microsoft refresh token | Server only | Renewing Graph access |
MCP access token | Client | Authenticating tool calls |
MCP refresh token | Client | Renewing MCP access |
Refresh Token Rotation
MCP refresh tokens are rotated on every use with family-based revocation:

How family revocation works:
Each token pair shares a familyId (format: tkfam_...) and a generation counter. On every refresh:
Server checks
usedAt— if already set, the token was reusedIf reused → entire family revoked immediately (all tokens with same
familyIddeleted)If valid → token marked used, new token issued with incremented
generation
This detects scenarios where an attacker obtains a refresh token and uses it while the legitimate client still holds the original. Whichever uses it second triggers the revocation.
Webhook Validation
Microsoft Graph webhook notifications are validated using the clientState field:

Validation details:
MICROSOFT_WEBHOOK_SECRETis a 128-character random string (openssl rand -hex 64)Set as
clientStatewhen creating Graph subscriptionsReturned unchanged in every webhook payload from Microsoft
Notifications with an invalid
clientStateare rejected before any processing
Rate Limiting
Rate limiting is expected to be handled at the infrastructure/ingress layer (e.g., Kong ingress controller), not within the application itself. The following limits are recommended:
Scope | Recommended Limit | Layer | Purpose |
|---|---|---|---|
Global (all endpoints) | 10 requests / 60 seconds | Ingress (Kong) | General brute-force protection |
Authorization endpoint | 3 requests / 60 seconds | Ingress (Kong) | Tighter protection for auth initiation |
Secret Management
Required Secrets
For the full secrets reference (format, generation, and description), see Configuration — Required Secrets.
Secret | Rotation Impact |
|---|---|
| All users must reconnect |
| Only mid-OAuth-flow sessions invalidated |
| Update + restart only (zero-downtime) |
| All subscriptions must be recreated |
Rotation Procedures
For detailed secret rotation procedures, see Authentication — Secret Rotation.
Key security considerations for rotation:
ENCRYPTION_KEYandMICROSOFT_WEBHOOK_SECREThave no zero-downtime rotation path — plan these as maintenance windows and notify users in advance.AUTH_HMAC_SECRETis the lowest-impact secret to rotate — existing connections are unaffected and only mid-OAuth-flow users are affected.MICROSOFT_CLIENT_SECRETsupports zero-downtime rotation because Microsoft allows multiple active client secrets simultaneously.
Security Checklist for Operators
See Operator Manual — Security Checklist for the full pre-production checklist.
Related Documentation
Architecture - Token isolation, storage, and authentication layers
Flows - OAuth connection and token refresh flows
Permissions - Microsoft Graph permissions and consent
Configuration - Secret configuration reference
Standard References
RFC 7636 - PKCE - Proof Key for Code Exchange
RFC 6749 - OAuth 2.0 - OAuth 2.0 Authorization Framework
OAuth 2.1 - OAuth 2.1 specification
NIST SP 800-38D - AES-GCM specification