jwt-auth
Verifies an HMAC-signed JWT taken from a request header, enforcing the signature and the exp claim, and makes the verified claims available to downstream nodes via context.message. Place it early in the request pipeline, before the upstream node.
Configuration
| Key | Type | Default | Description |
|---|---|---|---|
secret | string | — | Shared HMAC secret used to verify token signatures (inline mode). When set, every token is verified with it. Optional when use_consumers is set. |
use_consumers | boolean | false | Resolve the token's key claim against the gateway's consumers: section (jwt-auth: {key, secret, algorithm}) and verify with the matched consumer's own secret/algorithm, attaching the consumer on success. At least one of secret / use_consumers must be provided. |
algorithm | string | HS256 | One of HS256, HS384, HS512, used for inline verification. Any other value (including asymmetric algorithms like RS256) is rejected at config load. |
header_name | string | authorization | Header the token is read from (compared case-insensitively). An optional Bearer prefix is stripped. |
# inline shared secret
- id: auth
type: jwt-auth
config:
secret: ${JWT_SECRET}
algorithm: HS256
header_name: authorization
# per-consumer verification
- id: auth
type: jwt-auth
config:
use_consumers: true
header_name: authorization
Behavior
The token is read from header_name (stripping a Bearer prefix if present), then verified. Expiry (exp) validation is always enabled.
- Inline secret (
secretset): the token is verified with the configured HMAC secret andalgorithm. - Consumer mode (
use_consumers: true): the token's payload is base64url-decoded (without trusting it) to read thekeyclaim, which selects a consumer; the signature is then verified with that consumer's storedsecretandalgorithm. On success the consumer identity is attached (consumer.*keys incontext.messageplusX-Consumer-*headers).
Both may be enabled together: the inline secret is tried first, then consumer resolution.
On success the context passes through the success port with the claims exposed to downstream nodes:
context.message["jwt_claims"]= the full decoded claims objectcontext.message["user_id"]= thesubclaim, when present (convenience copy)
On a missing token or any verification failure (bad signature, expired, malformed, unknown key claim), the plugin sets a rejection on the response and exits through the denied port:
context.response.status_code=401- Body:
{"error": "unauthorized", "message": "<reason>"}withcontent-type: application/json
Ports
jwt-auth declares three output ports: success, denied (a rejection is prepared), and error (never actually used — the plugin never fails). Like success, denied is a mandatory port: the policy compiler rejects any policy that leaves it unwired. Wire jwt-auth.denied straight to client so the prepared 401 reaches the caller instead of continuing into upstream:
edges:
- from: jwt-auth.success
to: upstream.in
- from: jwt-auth.denied
to: client.in
Errors
This node never fails at execution time: it always returns through success, so its error port is never taken.