Designing a Centralized Identity Gateway for 70+ Microservices
Auth architecture across heterogeneous actors: OIDC, mTLS, SAML, and OPA sidecars
#The Challenge of Decentralized Auth
In a rapidly growing service architecture, security starts to fray if auth logic is embedded inside individual applications. We faced a common distributed platform challenge: 70+ microservices written in different frameworks (Spring Boot, Micronaut, Node.js) with inconsistent token validation, SAML federations, and static API keys. This led to high maintenance overhead, auditing difficulties, and potential service-to-service vulnerability leaks.
#System Architecture Visualized

// Identity Gateway Topology: Ingress routing lanes leading to Envoy Proxies and Open Policy Agent (OPA) validation nodes.
#The Four-Lane Authentication Strategy
We designed a centralized identity gateway model that segregated traffic into four distinct lanes based on the calling actor type:
- ▶Lane 1: Internal Human Users (OIDC/OAuth2 authorization code flow with PKCE, managed via AWS Cognito).
- ▶Lane 2: External Enterprise Partners (SAML 2.0 federation mapping to fine-grained application roles).
- ▶Lane 3: Service-to-Service Communication (Mutual TLS (mTLS) with SPIFFE/SPIRE for cryptographically verifiable identity).
- ▶Lane 4: External Developers/System Integrators (HMAC signed requests and secure API tokens with rate-limiting policies).
Envoy and Open Policy Agent (OPA) Sidecar Model
To decouple authorization logic from application code, we introduced an Envoy proxy sidecar pattern. All ingress traffic to a microservice passes through Envoy, which delegates authorization requests to a local Open Policy Agent (OPA) sidecar process over gRPC. OPA evaluates access control based on policy rules written in Rego.
# Rego policy evaluating service-to-service access
package env.authz
default allow = false
# Allow access if client identity is explicitly authorized
allow {
input.attributes.request.http.headers["x-spiffe-client-id"] == "spiffe://prod.local/ns/billing/sa/payment-processor"
input.attributes.request.http.method == "POST"
input.attributes.request.http.path == "/v1/charges"
}
# Allow human developers with administrative credentials
allow {
claims := jwt_claims(input.attributes.request.http.headers["authorization"])
"platform-admin" in claims.roles
}#Eliminating Validation Latency: Introspection Caching
Querying an Identity Provider (IdP) for every microservice invocation would crush API performance. To solve this, we implemented JWT cryptographical verification using public keys (JWKS) cached in-memory at the gateway. For stateful API tokens, we utilized a low-latency Redis cache layer cluster with a write-through invalidation strategy, dropping validation overhead from ~80ms to less than 2ms.