Skip to main content
Version: 3.10.x

a7-plugin-jwt-auth

Overview

The jwt-auth plugin authenticates requests using JSON Web Tokens. Consumers register a key and secret (or public key for asymmetric algorithms). Clients include a signed JWT in the request header, query parameter, or cookie. API7 EE validates the signature and claims, then forwards the request with consumer identity headers.

When to Use

  • Token-based stateless authentication
  • Asymmetric key verification (RS256, ES256, EdDSA) where API7 EE only needs the public key
  • Custom claims-based consumer identification
  • Integration with external token issuers (your own auth server, Auth0, etc.)

Consumer Credential Reference

FieldTypeRequiredDefaultDescription
keystringYesUnique identifier in JWT payload to match consumer
secretstringConditionalShared secret for HMAC algorithms (HS256/HS384/HS512). Encrypted in the database.
public_keystringConditionalPEM public key for RSA/ECDSA/EdDSA algorithms
algorithmstringNo"HS256"Signing algorithm (see supported list below)
expintegerNo86400Token lifetime in seconds (not UNIX timestamp)
base64_secretbooleanNofalseSet true if secret is base64-encoded
lifetime_grace_periodintegerNo0Clock skew tolerance in seconds

Supported Algorithms

FamilyAlgorithms
HMACHS256, HS384, HS512
RSARS256, RS384, RS512
RSA-PSSPS256, PS384, PS512
ECDSAES256, ES384, ES512
EdDSAEdDSA

Route/Service Configuration Reference

FieldTypeRequiredDefaultDescription
headerstringNo"authorization"Header to extract JWT from
querystringNo"jwt"Query parameter to extract JWT from
cookiestringNo"jwt"Cookie to extract JWT from
hide_credentialsbooleanNofalseRemove JWT before forwarding upstream
key_claim_namestringNo"key"JWT claim containing the consumer key
anonymous_consumerstringNoConsumer for unauthenticated requests
claims_to_verifyarrayNoClaims to require and verify (exp, nbf). Set this explicitly because behavior when omitted varies by gateway version.

Token Lookup Priority

  1. Header (default: authorization) — supports Bearer <token> prefix
  2. Query parameter (default: jwt)
  3. Cookie (default: jwt)

Step-by-Step: Enable jwt-auth with HS256

Replace <gateway-group-id> with the ID returned by a7 gateway-group list -o json.

1. Create a consumer

a7 consumer create -g <gateway-group-id> -f - <<'EOF'
{
"username": "alice"
}
EOF

2. Add jwt-auth credential

a7 credential create cred-alice-jwt -g <gateway-group-id> \
--consumer alice \
--plugins-json '{"jwt-auth":{"key":"alice-key","secret":"alice-secret-minimum-32-chars-long","algorithm":"HS256","exp":86400}}'

3. Create a service and route with jwt-auth

a7 service create -g <gateway-group-id> -f - <<'EOF'
{
"id": "jwt-protected-service",
"name": "JWT protected service",
"upstream": {
"type": "roundrobin",
"nodes": [{"host": "backend", "port": 8080, "weight": 1}]
}
}
EOF

a7 route create -g <gateway-group-id> -f - <<'EOF'
{
"id": "jwt-protected",
"paths": ["/api/*"],
"service_id": "jwt-protected-service",
"plugins": {
"jwt-auth": {}
}
}
EOF

4. Generate a JWT and test

Create a JWT with payload {"key": "alice-key", "exp": <future_timestamp>} signed with alice-secret-minimum-32-chars-long using HS256.

curl -i http://127.0.0.1:9080/api/test \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."

Step-by-Step: Enable jwt-auth with RS256

1. Generate RSA key pair

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

2. Create a consumer

a7 consumer create -g <gateway-group-id> -f - <<'EOF'
{
"username": "bob"
}
EOF

3. Create a credential with the public key

Save the following as bob-rs256-credential.yaml, replacing the placeholder with the base64 body between the PEM delimiters in public.pem:

plugins:
jwt-auth:
key: bob-key
algorithm: RS256
public_key: |
-----BEGIN PUBLIC KEY-----
replace-with-the-base64-body-from-public.pem
-----END PUBLIC KEY-----
a7 credential create cred-bob-jwt -g <gateway-group-id> --consumer bob -f bob-rs256-credential.yaml

Keep the private key outside API7 Gateway.

Sign tokens with private.pem externally. API7 EE only needs the public key.

Common Patterns

Custom claim name (use iss instead of key)

# Credential config (the key value identifies the consumer):
{
"jwt-auth": {
"key": "my-issuer-id",
"secret": "my-secret"
}
}

# Route config:
{
"jwt-auth": {
"key_claim_name": "iss"
}
}

# JWT payload:
{
"iss": "my-issuer-id",
"exp": 1879318541
}

Clock skew tolerance

{
"jwt-auth": {
"key": "consumer-key",
"secret": "my-secret",
"lifetime_grace_period": 30
}
}

Allows 30 seconds clock drift between token issuer and API7 EE.

Token in query parameter

{
"plugins": {
"jwt-auth": {
"query": "token"
}
}
}

Client sends: curl "http://127.0.0.1:9080/api/test?token=eyJ..."

Secret management with environment variables

{
"jwt-auth": {
"key": "consumer-key",
"secret": "$env://JWT_SECRET"
}
}

Secret management with HashiCorp Vault

{
"jwt-auth": {
"key": "consumer-key",
"secret": "$secret://vault/jwt/consumer-name/jwt-secret"
}
}

Headers Added to Upstream

HeaderValue
X-Consumer-UsernameConsumer's username
X-Credential-IdentifierCredential ID
X-Consumer-Custom-IdConsumer's labels.custom_id (if set)

Error Responses

HTTP CodeMessageCause
401"Missing JWT token in request"No token in header/query/cookie
401"JWT token invalid"Malformed token
401"failed to verify jwt"Bad signature, expired, or invalid claims
401"Invalid user key in JWT token"Consumer key not found

Troubleshooting

SymptomCauseFix
401 "failed to verify jwt"Token expired while exp verification is enabledGenerate new token with future exp
401 "failed to verify jwt"Algorithm mismatchEnsure credential algorithm matches token
401 "Invalid user key"Wrong claim nameSet key_claim_name on the route or service and include that claim in the JWT
Public key rejectedMissing newlines in PEMInclude \n after header/before footer lines
Clock skew errorsTime driftSet lifetime_grace_period on credential

Config Sync Example

Save the following as jwt-auth.yaml:

version: "1"
services:
- id: jwt-protected-service
name: JWT protected service
upstream:
type: roundrobin
nodes:
- host: backend
port: 8080
weight: 1
routes:
- id: jwt-protected
name: JWT protected route
paths:
- /api/*
service_id: jwt-protected-service
plugins:
jwt-auth: {}

Validate and apply this partial configuration to the target gateway group:

a7 config validate -f jwt-auth.yaml
a7 config sync -g <gateway-group-id> -f jwt-auth.yaml --delete=false

Note: Create the consumer and credential separately with a7 consumer create and a7 credential create. Config Sync manages only the service and route in this example. Disabling deletion preserves other resources that are not included in this partial configuration.


This page is generated from a7-plugin-jwt-auth/SKILL.md in the api7/a7 repository. Browse all skills on the AI Agent Skills page.