Auth in Microservices: Service-to-Service Identity, mTLS, and SPIFFE/SPIRE
Auth in Microservices
Monoliths have one trust boundary: the request arrives authenticated or it does not. Microservices have N trust boundaries, where N grows with every service you add. Each service-to-service call is a security decision: does this caller have the identity it claims? Does it have permission to make this request? Is it acting on behalf of a user, and if so, which one?
The naive approach: share a secret (API key, static token) between services. Every service that needs to call another service gets the secret. The secret is stored in environment variables, configuration files, or a secrets manager. This works until the first secret leaks. One compromised service means every service it could call is also compromised, because the shared secret grants identical access regardless of which process presents it.
The correct approach: each service has its own cryptographic identity. Authentication is mutual (both sides verify each other). Authorization is scoped (a token for Service A does not work at Service B). User context is propagated without sharing credentials.
The Boundaries in the SaaS Platform
The topology shows every trust boundary in the platform’s service mesh. Each arrow represents a point where caller identity must be verified and user context must be propagated. The API Gateway handles user-facing authentication and fans out to internal services. Internal boundaries use mTLS for transport-level identity and token exchange for user-context propagation. The Analytics Service sits behind Core API with read-only access, enforcing an additional authorization boundary.
Each arrow is a trust boundary. Each boundary requires:
-
Caller identity verification. The receiving service must verify that the caller is who it claims to be. For user-facing boundaries, this is token validation. For service-to-service boundaries, this is mTLS or client credentials.
-
User context propagation. When the API Gateway calls Core API on behalf of user Alice, Core API needs Alice’s identity and tenant context. The user’s JWT cannot be forwarded directly (wrong audience claim). Token exchange creates a new token scoped to the downstream service.
-
Scope limitation. The Analytics Service only needs read access. A token exchange for Analytics should not include write scopes even if the original user has them.
Authentication Mechanisms by Boundary
| Boundary | Mechanism | User context |
|---|---|---|
| Browser → API Gateway | Opaque token (CH6) | Direct (user authenticates) |
| API Gateway → Core API | Token exchange + mTLS | Propagated via exchanged token |
| Core API → Billing | Client credentials + mTLS | Propagated via act claim |
| Core API → Notification | Client credentials + mTLS | Propagated via message payload |
| Core API → Analytics | Token exchange (read scope only) | Propagated, scope-reduced |
mTLS handles transport-level identity: “this TCP connection is from Core API.” Token exchange handles application-level authorization: “Core API is calling on behalf of Alice from acme-corp with payment:process scope.”
What This Chapter Covers
Section 1 covers mTLS and SPIFFE/SPIRE: how to give each service instance a cryptographic identity that rotates automatically, without shared secrets, with kernel-level attestation that the calling process is the workload it claims to be.
Section 2 covers user context propagation: why forwarding JWTs breaks audience validation, how token exchange (RFC 8693) solves the problem correctly, and when to use client credentials (service acting on its own behalf, not on behalf of a user).