centralizing authentication
Anyone implementing authentication separately in every GraphQL subgraph ends up building the same JWT check ten times, with ten potential points of failure. An API gateway handles token validation, session management, and rate limiting in one place, and forwards only a verified auth context to the services behind it.
Table of Contents
- 1. Why auth shouldn't be duplicated in every service
- 2. API gateway types for GraphQL at a glance
- 3. JWT validation centralized at the gateway
- 4. Forwarding auth context to subgraphs
- 5. Session-based vs. token-based auth at the gateway
- 6. Combining rate limiting with auth
- 7. Practical example: Apollo Router with a coprocessor
- 8. Gateway pattern in front of Magento GraphQL
- 9. Gateway auth patterns compared
- 10. Summary
- 11. FAQ
1. Why auth shouldn't be duplicated in every service
As soon as a GraphQL system consists of multiple subgraphs or microservices, the question arises of where authentication gets checked. The obvious but problematic answer is: in every single service. That leads to the same JWT validation logic, the same secret handling, and the same error handling being duplicated across five, ten, or twenty codebases. An API gateway solves exactly this problem by centralizing authentication at a single, clearly defined point, before a request ever reaches one of the GraphQL services behind it.
The benefit of a centralized API gateway pattern shows up especially during security updates: if a vulnerability is found in the JWT library, only the gateway needs updating, not twenty services in parallel. At the same time, the risk drops that an individual service accidentally implements a weaker or buggy auth check, because the check itself no longer lives in the service at all. The following sections show concrete patterns for how an API gateway takes on this responsibility in GraphQL architectures.
2. API gateway types for GraphQL at a glance
Three basic shapes of an API gateway show up most often in GraphQL architectures. The simple reverse proxy forwards requests unchanged to a single GraphQL endpoint and only handles TLS termination, header injection, and basic auth checks, for example Nginx or Envoy in front of a monolithic Magento GraphQL endpoint. The federation gateway, such as Apollo Router or GraphQL Mesh, composes multiple subgraph schemas into a single, publicly visible supergraph and distributes sub-queries to the responsible subgraphs.
The third type, the Backend-for-Frontend gateway (BFF), differs fundamentally: rather than bundling multiple GraphQL services into one schema, it provides a tailored GraphQL schema for each frontend type, for example a mobile app and a web storefront, that internally aggregates multiple backend systems. All three API gateway types can centralize authentication, but they differ in how granularly they can make auth decisions per field or per subgraph.
3. JWT validation centralized at the gateway
The most common form of centralized authentication at an API gateway is JWT validation: the gateway checks the signature, expiration, and issuer of a submitted bearer token before the request is ever forwarded to a GraphQL resolver. If validation fails, the gateway responds directly with 401 Unauthorized, without any subgraph ever learning about the request. That reduces attack surface, because invalid tokens never reach the internal services in the first place.
It's important that the gateway caches the identity provider's public key and refreshes it periodically via the JWKS endpoint, rather than making a fresh request to the auth server on every call. This separation between token issuance, usually handled by a dedicated identity provider such as Keycloak or Auth0, and token validation at the API gateway is a core principle of modern auth architectures: the identity provider knows users and passwords, the gateway only knows the public key for signature verification.
// gateway-jwt-middleware.js — validates JWTs before requests reach any subgraph
const { createRemoteJWKSet, jwtVerify } = require('jose');
const JWKS = createRemoteJWKSet(new URL('https://auth.mironsoft.de/.well-known/jwks.json'));
async function validateToken(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing bearer token' });
}
try {
const token = authHeader.slice(7);
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://auth.mironsoft.de',
audience: 'graphql-api',
});
// Attach verified claims — subgraphs trust this, never re-validate the raw token
req.authContext = { userId: payload.sub, roles: payload.roles ?? [] };
next();
} catch (err) {
// Signature invalid, expired, or wrong issuer — reject before reaching any subgraph
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
module.exports = { validateToken };
4. Forwarding auth context to subgraphs
After successful JWT validation, the API gateway must forward the verified information to the subgraphs behind it in a way they can trust without re-validating the token themselves. The usual pattern: the gateway extracts the user ID and roles from the validated token and sets them as internal, signed headers such as X-User-Id and X-User-Roles, before forwarding the request internally. Subgraphs check these headers, not the original JWT, because they run in a trusted internal network behind the gateway.
A critical security aspect here: these internal headers must never be settable directly by external clients. If a subgraph runs in a network that is theoretically also reachable from outside, the gateway must actively strip incoming X-User-Id headers before setting its own verified values. Otherwise, an attacker bypassing the gateway could assign themselves arbitrary roles. In Apollo Federation, the router handles this context propagation through rhai scripts or coprocessor hooks that run before every subgraph request.
{
"_comment": "Decoded JWT payload the gateway validates before forwarding a request",
"sub": "customer-48213",
"iss": "https://auth.mironsoft.de",
"aud": "graphql-api",
"roles": ["customer", "newsletter-subscriber"],
"exp": 1798761600,
"_forwarded_headers": {
"X-User-Id": "customer-48213",
"X-User-Roles": "customer,newsletter-subscriber",
"_note": "set only by the gateway, stripped from any incoming external request"
}
}
5. Session-based vs. token-based auth at the gateway
Besides JWT-based authentication, some API gateway architectures rely on classic, server-side sessions, especially when an existing system such as Magento already uses session cookies for the storefront. In this model, the gateway checks the session cookie against a central session store, usually Redis, and loads the associated user data from there instead of decoding it directly from a token. The advantage: sessions can be invalidated server-side immediately, for example on logout or a ban, while a once-issued JWT stays valid until it expires, unless an additional revocation list is maintained.
The downside of session-based auth at the gateway is the extra network hop to the session store on every request, while a JWT can be validated purely locally. In practice, many production API gateway setups combine both models: short-lived access tokens in JWT format for fast, stateless validation, combined with a server-side session or a refresh-token store for the ability to revoke access at any time.
6. Combining rate limiting with auth
An often overlooked benefit of a centralized API gateway: rate limiting can be tied directly to the verified identity rather than just the IP address. An authenticated user gets a higher quota than an anonymous request, and different roles, for example paying customers versus guests, can receive different limits. This combination of auth checking and rate limiting only works if both sit at the same point in the pipeline, otherwise rate limiting has no idea about user identity in the first place.
For GraphQL, an additional dimension comes into play: rate limiting shouldn't just count the number of requests, it should also account for query complexity, because a single, deeply nested GraphQL query can generate more backend load than a hundred simple REST calls. An API gateway that combines JWT validation, role detection, and complexity-based rate limiting prevents both unauthorized access and resource exhaustion from authorized but excessively complex queries.
#!/usr/bin/env bash
# test-gateway-auth.sh — verify the gateway rejects invalid tokens before subgraphs see them
set -euo pipefail
GATEWAY_URL="https://api.mironsoft.de/graphql"
QUERY='{"query":"{ product(sku: \"TEST-001\") { name } }"}'
echo "[TEST] Request without token — expect 401"
curl -s -o /dev/null -w "%{http_code}\n" -X POST "$GATEWAY_URL" \
-H "Content-Type: application/json" -d "$QUERY"
echo "[TEST] Request with expired token — expect 401"
curl -s -o /dev/null -w "%{http_code}\n" -X POST "$GATEWAY_URL" \
-H "Authorization: Bearer $EXPIRED_TOKEN" \
-H "Content-Type: application/json" -d "$QUERY"
echo "[TEST] Request with valid token — expect 200"
curl -s -o /dev/null -w "%{http_code}\n" -X POST "$GATEWAY_URL" \
-H "Authorization: Bearer $VALID_TOKEN" \
-H "Content-Type: application/json" -d "$QUERY"
7. Practical example: Apollo Router with a coprocessor
Apollo Router, the modern, Rust-based federation gateway, offers a clear path via its coprocessor mechanism to plug in authentication as an external HTTP service instead of hard-wiring it into the router binary. Before every subgraph request, the router calls a configured HTTP endpoint, passes headers and context, and expects back whether the request may proceed and which auth context to inject. This fully decouples the auth logic from the router itself and allows implementing it in any language.
Alternatively, Apollo Router supports embedded rhai scripts for lightweight logic directly inside the router process, without the network overhead of an external coprocessor call. For pure JWT validation without complex additional logic, the router's built-in authentication plugin is usually sufficient and more performant than an external coprocessor, while more complex scenarios such as dynamic role mapping from an external identity provider favor the coprocessor approach.
# router.yaml — Apollo Router: built-in JWT auth plus a coprocessor for custom logic
authentication:
router:
jwt:
jwks:
- url: https://auth.mironsoft.de/.well-known/jwks.json
header_name: Authorization
header_value_prefix: "Bearer "
coprocessor:
url: http://auth-coprocessor:8081
router:
request:
headers: true
subgraph:
all:
request:
headers: true
context: true
rate_limit:
# Complexity-aware limiting combined with the authenticated identity
global:
capacity: 1000
interval: 60s
per_user:
capacity: 200
interval: 60s
# Federated query — the router splits this across subgraphs, injecting the
# same verified auth context into each subgraph request automatically
query CustomerDashboard {
customer {
# resolved by the "accounts" subgraph, using X-User-Id from the gateway
id
email
orders {
# resolved by the "orders" subgraph, same trusted context, no re-auth
id
total
status
}
}
}
8. Gateway pattern in front of Magento GraphQL
Magento's own GraphQL endpoint ships with its own authentication via customer tokens and admin tokens, but in production headless architectures an API gateway often sits in front of it anyway, combining multiple backend systems, for example Magento for product data and a separate CMS for content, behind a single supergraph. In this setup, the gateway validates the user token once and then either forwards it unchanged to Magento, which performs its own token check, or exchanges it for an internal Magento customer token that only the gateway knows.
The second approach, token exchange at the gateway, has an important security advantage: the publicly visible access token a frontend receives differs from the internal Magento token, which never leaves the gateway. Even if the public token were compromised, internal Magento access would stay protected, as long as the token exchange logic itself is implemented securely. For Magento projects with multiple frontend types, this pattern is the standard way to avoid reimplementing authentication in every individual headless client.
9. Gateway auth patterns compared
The table below compares the most important patterns for centralized authentication at a GraphQL API gateway.
| Pattern | Advantage | Downside |
|---|---|---|
| JWT validation at the gateway | Stateless, fast, no session store lookup | No instant revocation without an extra mechanism |
| Session-based at the gateway | Instant invalidation possible | Extra network hop per request |
| External coprocessor | Language- and team-independent, flexibly extensible | Extra network latency per subgraph request |
| Token exchange at the gateway | Internal token stays isolated from the frontend | Extra complexity in the gateway code |
No pattern is universally correct. JWT validation fits most performance-critical scenarios, while session-based auth makes sense where instant revocability is mandatory, for example admin access. Token exchange pays off especially in multi-backend architectures with Magento and other systems behind a shared API gateway.
Mironsoft
GraphQL gateway architecture and auth concepts
Centralizing authentication for your GraphQL landscape?
We design and implement API gateway architectures for GraphQL, from JWT validation through token exchange to complexity-based rate limiting, including integration with Magento and other backend systems.
Gateway design
Choose the right gateway type and auth pattern for your architecture
Apollo Router setup
Configure JWT auth, coprocessors, and rate limiting for production
Magento integration
Set up token exchange between the gateway and Magento customer tokens
10. Summary
A centralized API gateway solves authentication for GraphQL architectures at exactly one point, instead of duplicating it in every subgraph. JWT validation with cached JWKS keys usually forms the base, complemented by securely forwarded auth context in internal headers that external clients cannot set directly. Session-based auth stays relevant where instant revocability matters, and can be combined with JWT-based access tokens.
Apollo Router, with its coprocessor and rhai mechanisms, shows how auth logic can be decoupled from the gateway infrastructure without sacrificing performance. For Magento projects with multiple frontend types, token exchange at the gateway is the most effective pattern for isolating internal Magento access from the publicly visible token. Combined with complexity-based rate limiting, this produces an API gateway that reliably prevents both unauthorized access and resource exhaustion.
GraphQL API Gateway Patterns for Auth — Key Takeaways
JWT validation
Check centrally at the gateway, use cached JWKS keys, reject invalid tokens early.
Auth context
Forward only internally set, verified headers to subgraphs, never externally overridable.
Rate limiting
Tie to identity and query complexity, not just the IP address.
Token exchange
Separate the public token from the internal backend token, for example with Magento integrations.