Skip to content

Integration Security & Governance

Every integration needs to answer two questions before it ships: “how does the other side prove who it is,” and “who’s watching for this contract quietly breaking someone downstream?”

  • What it is: The controls that keep cross-system integrations safe and well-behaved: OAuth2 client credentials (a system authenticates as itself, not as a user, to call another system), mTLS (mutual TLS — both sides present certificates to verify each other, not just the client verifying the server), rate limiting/throttling (capping how often a consumer can call an API), and contract governance (a process ensuring API/event changes don’t silently break existing consumers).
  • Why it matters: An integration with no auth is an open door; one with no rate limit is one noisy consumer away from taking down a shared system; one with no contract governance is a breaking change away from silently failing every downstream consumer at once.
  • For developers: before exposing or consuming any cross-system API, check it has machine-to-machine auth (not a shared static API key), a rate limit, and a documented deprecation policy — these are cheap to add early and expensive to retrofit after the first outage.
  • When to use it: Every integration that crosses a system, team, or organizational boundary.
  • When not to use it: N/A — some level of auth and rate limiting is appropriate for essentially every integration; the degree of governance formality can scale down for low-risk internal integrations.
  • Prerequisites: API-Led Connectivity.
graph LR
    S[Order Service] -- OAuth2 client credentials + mTLS --> GW[API Gateway]
    GW -- rate limit + auth check --> P[Partner Shipping API]
    GW --> M[Monitoring / Contract Governance]

The gateway is the checkpoint: it verifies identity, enforces rate limits, and is the natural place to observe whether consumers are honoring the contract.

  1. Choose a machine-to-machine auth method: OAuth2 client credentials for most API calls, mTLS in addition when the connection itself (not just the request) needs mutual verification (e.g. high-trust partner links).
  2. Configure rate limiting/throttling at the gateway per consumer, not just globally, so one noisy consumer can’t starve others.
  3. Version every contract (API or event schema) and communicate a deprecation window before removing an old version.
  4. Set up monitoring on the gateway/broker for auth failures, rate-limit rejections, and contract/schema validation errors.
  5. Establish a lightweight governance checkpoint (even just a peer review) for any breaking contract change before it ships.

The internal Order service needs to call a partner’s Shipping API to schedule deliveries. The partner requires strong assurance of identity, and the internal platform team needs visibility into how much traffic the Order service is sending.

  • Input: a planned direct call from Order service to the partner Shipping API with no agreed auth mechanism and no rate limiting.
  • Process: the Order service authenticates using OAuth2 client credentials to get a short-lived token, and the connection itself uses mTLS so both sides present certificates. The call is routed through an API gateway that enforces a per-consumer rate limit and logs every call for monitoring dashboards. The Shipping API’s contract is versioned, and the partner commits to a deprecation window before retiring any version.
  • Output: the integration has verifiable machine identity on both sides, protection against one runaway process overwhelming the partner, and a documented path for future contract changes instead of surprise breakage.
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
var token = await app.AcquireTokenForClient(new[] { "api://shipping-partner/.default" })
.ExecuteAsync(); // OAuth2 client credentials flow
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);

Microsoft Entra ID issues the client-credentials token, and Azure API Management enforces the per-consumer rate limit and mTLS certificate check before the request reaches the partner.

resource apimPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-09-01' = {
name: 'pe-apim-internal'
location: location
properties: {
subnet: { id: subnetId }
privateLinkServiceConnections: [
{ name: 'apim-connection', properties: { privateLinkServiceId: apim.id, groupIds: ['Gateway'] } }
]
}
}

The internal process and system API layers sit behind this private endpoint with no public exposure at all, while the externally-published experience API is exposed through Azure Front Door with a WAF policy in front of it — two different trust boundaries, each getting the control appropriate to it.

Use case Control Notes
Service-to-service API calls, no end user involved OAuth2 client credentials Standard machine-to-machine auth flow
High-trust partner or regulated data link mTLS (in addition to OAuth2) Verifies both endpoints, not just the caller’s token
Shared API with multiple internal or external consumers Rate limiting per consumer Prevents one consumer from degrading service for others
API or event schema needs to change Contract governance + versioning Gives existing consumers a deprecation window instead of a surprise break
  • Token scope: grant OAuth2 client credentials tokens the minimum scope needed for the specific integration, not a broad “full access” token reused everywhere.
  • mTLS overhead: mutual TLS adds certificate management overhead — reserve it for connections where verifying both endpoints is worth that cost, rather than applying it universally by default.
  • Governance without bureaucracy: contract governance can be as lightweight as a required schema-diff check in CI that flags breaking changes — it doesn’t require a heavyweight review board for every change.
Term Meaning
OAuth2 client credentials An OAuth2 flow where a system authenticates as itself, not on behalf of a user
mTLS Mutual TLS — both client and server present certificates to verify each other
Rate limiting / throttling Capping how often a consumer can call an API in a given period
Contract governance The process ensuring API/event contract changes don’t silently break consumers
Symptom Likely cause Fix
A shared API keeps degrading during traffic spikes from one consumer No per-consumer rate limiting Add rate limits scoped per consumer, not just a global limit
A partner integration silently breaks after a schema change No contract governance / versioning Add a schema-diff check in CI and a deprecation window policy
Static API keys shared across multiple services No machine identity per caller Move to OAuth2 client credentials so each caller has its own revocable identity
  • Why is mTLS described as verifying “both sides,” compared to a typical browser HTTPS connection?
  • What would you check in the worked example to confirm the rate limit is actually per-consumer and not global?

Part of Introduction to Integration Architecture. Builds on API-Led Connectivity; see also Integration Platforms & Middleware for where gateways fit, and the Glossary for term definitions.