Protected Routes
tsdevstack provides guards and decorators for protecting API endpoints. Kong handles token validation at the gateway level, while AuthGuard provides additional protection and user context in your services.
How it works
AuthGuard is applied globally in all services via APP_GUARD. You don't need to add it per endpoint.
Two-layer authentication
Authentication happens at two independent layers, controlled by different decorators:
Important: Without @ApiBearerAuth(), Kong treats the route as public (no JWT required). But AuthGuard still rejects anonymous callers unless you also add @Public(). On a @Public() handler AuthGuard still identifies callers that present valid credentials; it just doesn't require them.
For fully public endpoints (login, signup): use @Public() and omit @ApiBearerAuth().
Trust first
Kong adds a secret trust token (X-Kong-Trust) to every request it forwards. AuthGuard checks it before it looks at anything else:
- Valid trust token: the request came through Kong, and AuthGuard reads the identity Kong set.
X-Userinfo(from the JWT, validated by Kong's OIDC plugin) makes the caller a logged-in user. A partner API key validated by Kong'stsdevstack-api-keyplugin, which sendsX-Api-Key-IdandX-Api-Key-Consumer, makes it a partner. - No trust token: identity headers are ignored, whatever they say. The request is either an internal service-to-service call that authenticates with the service
API_KEY(x-api-keyheader), an anonymous call to a@Public()handler, or it gets 401. - Wrong trust token: 401. The exceptions are
/health,/metricsand/.well-known/paths, which load balancers and Kong call without the token; there the token and all identity headers are ignored.
Two consequences worth knowing:
- Calling a service directly (for example
http://localhost:3001instead of the gateway athttp://localhost:8000) with a bearer token gets 401 on protected handlers. Kong validates JWTs, not the services, so a JWT that never went through Kong proves nothing. The same applies to Swagger UI's "Try it out", end-to-end tests that call service ports, and sidecars. Go through the gateway, or use the serviceAPI_KEYfor internal calls. - Forged identity headers never reach your code. Kong removes client-sent identity headers (
X-Userinfo,X-Consumer-*,X-Credential-Identifier,X-Kong-Trustand similar) before authentication, with the framework plugintsdevstack-strip-identity. Behind that, AuthGuard only trusts identity headers on requests with a valid trust token, and treats a request that claims to be both a user and a partner key as forged (401).
What AuthGuard puts on the request
Public endpoints
Mark endpoints that don't require authentication:
Accessing user information
Use @Req() decorator with AuthenticatedRequest type:
The KongUser type allows any JWT claim:
Controller-level public
Make all endpoints in a controller public:
Partner API endpoints
For API key authentication (instead of JWT). Keys are created and managed at runtime through the auth-service admin API and checked by the gateway; see API Keys.
Rules AuthGuard applies to partner keys:
- A partner key works only on
@PartnerApi()handlers. On any other handler,@Public()ones included, it gets 403. Kong already exposes only@PartnerApi()paths under/api/...(see Gateway Routing); this is the second line of defence. req.useris never set for partner requests. Use@Partner()or@ApiKey()to know who is calling.- The gateway checks the key, its expiry and its limits before the request reaches you, and never forwards the key itself. Your service receives
X-Api-Key-IdandX-Api-Key-Consumerinstead, andAuthGuardonly reads them on requests with a valid trust token. - A
@PartnerApi()handler also accepts logged-in users and internal service calls, which is how dual-access endpoints work. Checkreq.authTypewhen the handler needs to tell them apart.
Note:
req.serviceidentifies the request source:'partner'for partner API key requests, the caller's service name (or'internal') for service-to-service calls.
Role-based access
Restrict handlers to system or custom roles with @Roles():
Callers without one of the listed roles get 403. See Roles.
Custom guards
Create custom guards for specific requirements:
Use the custom guard alongside AuthGuard:
OpenAPI documentation
Add Swagger decorators to document auth requirements:
See Two-layer authentication above for details on how these decorators interact.
How Kong and AuthGuard work together
- Kong strips client-sent identity headers (
tsdevstack-strip-identity) - Kong validates the JWT using the OIDC plugin (or the partner key on
/api/...routes) - Kong passes the identity to the service (
X-Userinfowith the JWT claims) and adds the trust token - AuthGuard verifies the trust token, then classifies the caller as user, partner key or internal service
- AuthGuard populates
req.user(orreq.apiKey) and enforces@Public()and@PartnerApi() - Your code accesses
req.userwith full type safety