Who Is Responsible for Auth in a Microservices Architecture
by Eric Hanson, Backend Developer at Clean Systems Consulting
The gap that produces security incidents
Your organization has eight services. Each team owns one. Each team has independently decided how to handle authentication and authorization. Team A validates JWTs using RS256. Team B validates them using HS256 with a secret that three developers know. Team C doesn't validate tokens on internal endpoints because "those are internal only." Team D added auth six months after launch and the implementation is inconsistent across endpoints.
This is not an extreme scenario. It's the default outcome when auth responsibility is not explicitly assigned in a microservices architecture. The result is inconsistent security posture, duplicated implementation work, and at least one team that's made a decision they don't realize is wrong.
Authentication versus authorization: different problems, different owners
These are frequently conflated but require separate ownership decisions.
Authentication answers: is this request from who it claims to be? It involves verifying credentials, issuing tokens, validating token signatures, and managing sessions. This is a cross-cutting concern that should be owned centrally.
Authorization answers: is this authenticated identity allowed to perform this operation on this resource? This is domain-specific. Whether a user can delete an order depends on business rules that the Order Service owns. Whether a service account can call a payment endpoint is a policy decision that needs to be enforced close to the resource.
These require different ownership models because they have different change rates, different stakeholders, and different risk profiles.
Who owns authentication
Authentication — token issuance, validation standards, key management, session lifetime — belongs to a central platform team or a dedicated auth service. This is not a microservice in the business domain sense; it's infrastructure. It should be treated with the same discipline as your API gateway or your Kubernetes cluster.
The deliverables from the auth infrastructure:
- A token issuance endpoint (OAuth 2.0 authorization server: Keycloak, Auth0, or a custom implementation)
- A JWKS endpoint for public key distribution
- A shared validation library consumed by all services
- Documented token format: which claims are in every token, what they mean, what their expected values are
- Key rotation policy and tooling
All services use the central validation library. No service implements its own JWT parsing. When the signing algorithm changes or new required claims are added, the library is updated once and services adopt it on their next deploy.
Who owns authorization
Authorization logic belongs to the service that owns the resource. The User Service decides what user data different callers can access. The Payment Service decides which operations require which roles. The Order Service decides whether a given user can modify a given order.
The mistake is centralizing authorization: building a permissions service that every other service calls to check "can user X do action Y on resource Z?" This sounds clean architecturally but creates a synchronous dependency on your authorization service for every permission check in the system. If the permissions service is slow or unavailable, the entire system degrades.
Authorization checks should be local. Each service evaluates claims from the JWT (roles, permissions, user attributes) against its own authorization rules:
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.userId")
public Order getOrder(String orderId, String userId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
For more complex policies, Open Policy Agent (OPA) can be deployed as a sidecar, providing local policy evaluation without a network call to a central service:
# OPA policy: can this user access this order?
allow {
input.user.roles[_] == "admin"
}
allow {
input.user.id == input.resource.owner_id
input.action == "read"
}
OPA evaluates policies locally using data pushed to it — the policy evaluation itself is not a network call.
Service-to-service authorization
When services call each other, the authorization question is: is this service allowed to call this endpoint? This is different from user authorization.
The answer should be explicit, not implicit. Relying on "only internal services can reach this endpoint" (network isolation alone) is insufficient if a service is compromised. Use service identity claims in tokens:
// Service-to-service JWT claims
{
"sub": "order-service",
"aud": "payment-service",
"scope": "payments:create",
"iss": "https://auth.internal",
"exp": 1714089600
}
The Payment Service validates that the caller is order-service and has payments:create scope. Scope grants are managed centrally (in the auth service configuration) but enforced locally (by Payment Service).
Making ownership explicit
Write it down. Your architecture documentation should specify:
- Which team owns the authentication infrastructure
- Which team is responsible for updating the shared validation library
- How authorization decisions are made in each service
- Who approves service-to-service permission grants
- What the process is when a new service needs to call an existing protected endpoint
Unwritten ownership is no ownership. In practice, it means the first team to make a decision makes it for everyone — or the last team to be asked makes it inconsistently with what came before. Neither outcome is acceptable for security-critical infrastructure.