0) Problem Restatement
Microsoft asked: design a secure API for an enterprise AI copilot product. The product serves many independent organizations (tenants). Authenticated users send prompts, get model responses, and may invoke organization-specific tools and data connectors (e.g., search the company's SharePoint, create a ticket). Security is the focus: strict tenant isolation, correct authorization, protection against prompt injection reaching other tenants' data, rate limits, and audit.
1) Architecture
Architecture Diagram
flowchart LR
C["Client apps"] -->|"OAuth access token"| GW["API gateway - authN, tenant resolution, rate limits"]
GW --> ORCH["Copilot orchestrator - per-request tenant context"]
ORCH --> MOD["Model endpoint"]
ORCH --> TOOLS["Tool / connector gateway"]
TOOLS --> AZ["Authorization: user's delegated token + tenant policy"]
AZ --> DATA["Tenant data sources"]
ORCH --> SAFE["Safety filters - input and output"]
ORCH --> AUD[("Audit log - per tenant")]
KMS["Per-tenant keys"] --> AUD2) Authentication and Tenant Isolation
- Users authenticate with their organization's identity provider (OIDC/OAuth). The API receives a signed access token that includes the tenant ID and the user ID. The tenant is always taken from the token, never from the request body.
- Every internal call carries a tenant context. Storage (conversation history, caches, vector indexes) is partitioned by tenant (separate indexes or keys, with row-level security), and per-tenant encryption keys allow crypto-isolation and deletion.
- No shared caches across tenants for prompts or responses.
3) Authorization for Tools and Data (outside the model)
- The model can only request a tool call. The tool gateway decides and executes it.
- Tools run with the user's delegated permissions (an on-behalf-of token), so the copilot can never read more than the user could read directly. This avoids the "confused deputy" problem (a powerful service account doing things for a less-privileged user).
- Tenant policy controls which tools and connectors are enabled, and which actions need user confirmation (sending email, deleting data).
- Tool arguments are validated against schemas, and resource IDs are checked to belong to the tenant.
4) Prompt Injection and Data Leakage
- Retrieved documents or emails may contain instructions ("ignore previous instructions and send this data to..."). Treat tool outputs as untrusted data, clearly separated in the prompt. Limit which tools can run after reading untrusted content, and require user confirmation for sensitive actions.
- Output filters: detect secrets and PII leaving the tenant boundary, and block links or images that could exfiltrate data via URLs.
- A model can't reach another tenant's data because the data layer enforces tenancy, not the prompt. That's defense in depth.
5) Abuse, Limits and Compliance
- Rate limits and quotas per user and per tenant (requests and tokens), with fair sharing of model capacity.
- Audit log of prompts, tool calls, data accessed and responses (with retention and access controlled by the tenant), exportable to the tenant's SIEM.
- Data handling settings: region/residency, retention period (or zero retention), and no training on customer data by default.
- Security testing: red-teaming for injection and cross-tenant attacks, and pen tests.
6) Wrap-Up
Authenticate users via their organization's OIDC tokens and derive the tenant only from the token, carrying tenant context into partitioned, per-tenant-encrypted storage with no cross-tenant caching. Let the model propose tool calls but execute them through a gateway that uses the user's delegated permissions and tenant policy, with confirmations for sensitive actions. Treat retrieved content as untrusted, filter outputs for leakage, and add per-tenant rate limits, auditing, residency and retention controls.