Skip to main content

Version: 3.11.0

Implement JWT Authentication

JWT authentication is a secure and stateless method for authenticating API clients using JSON Web Tokens (JWTs). It involves issuing a signed token to a client after successful authentication, which the client includes in subsequent requests. JWTs encapsulate claims such as user identity and roles, enabling APIs to validate requests without querying a backend database. This approach is ideal for scalable, distributed applications requiring lightweight and fast authentication. However, since JWTs remain valid until they expire, care must be taken to use short expiration times or implement token revocation for added security.

In this guide, you will implement a scenario where there are two consumers using JWT authentication to authenticate with APISIX, each with a different rate limiting quota. Once implemented, consumers should have access to the upstream service and forward consumer IDs to the upstream service, opening up options for additional business logics.

Create Consumers

A consumer is an application or a developer who consumes the API. You should always create consumers when using APISIX built-in authentication methods.

Create a consumer johndoe with an optional custom ID and a rate limiting quota of one request in a 30-second window:

curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "johndoe",
"labels": {
"custom_id": "john-doe-junior"
},
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429
}
}
}'

The custom ID will be forwarded to the upstream service, should you wish to implement additional business logics.

Create another consumer janedoe with an optional custom ID and a rate limiting quota of two request in a 30-second window:

curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "janedoe",
"labels": {
"custom_id": "jane-doe-senior"
},
"plugins": {
"limit-count": {
"count": 2,
"time_window": 30,
"rejected_code": 429
}
}
}'

Create Consumer Credentials

Credentials are used to configure authentication credentials associated with consumers.

Create jwt-auth credential for johndoe:

curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-john-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "john-key",
"secret": "john-hs256-secret"
}
}
}'

Create jwt-auth credential for janedoe:

curl "http://127.0.0.1:9180/apisix/admin/consumers/janedoe/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jane-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jane-key",
"secret": "jane-hs256-secret"
}
}
}'

Create a Route

Create a route and enable jwt-auth:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-auth-route",
"uri": "/anything",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

Issue JWT

To issue a JWT for johndoe, you could use JWT.io's debugger or other utilities. If you are using JWT.io's debugger, do the following:

  • Select HS256 in the Algorithm dropdown.
  • Update the secret in the Verify Signature section to be john-hs256-secret.
  • Update payload with consumer key john-key; and add exp or nbf in UNIX timestamp.
note

If you are using API7 Enterprise, the requirement of exp or nbf is not mandatory. You can optionally include these claims and use the claims_to_verify parameter to configure which claim to verify.

Your payload should look similar to the following:

{
"key": "john-key",
"nbf": 1729132271
}

Copy the generated JWT under the Encoded section and save to a variable:

john_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.0VDKUzNkSaa_H5g_rGNbNtDcKJ9fBGgcGC56AsVsV-I

Repeat the step for janedoe and save the generated JWT to jane_jwt_token.

Verify

Send a request to the route with john's key:

curl -i "http://127.0.0.1:9080/headers" -H "Authorization: ${john_jwt_token}"

You should see an HTTP/1.1 200 OK response similar to the following:

{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
...
"X-Consumer-Username": "johndoe",
"X-Credential-Identifier": "cred-john-basic-auth",
"X-Consumer-Custom-Id": "john-doe-junior",
"X-Forwarded-Host": "127.0.0.1"
},
...
}

Generate three requests to the route with john's key:

resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'Authorization: ${john_jwt_token}' -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429

You should see the following response, showing that out of the 3 requests, 1 request was successful while the others were rejected:

200:    1, 429:    2

Generate three requests to the route with jane's key:

resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'Authorization: ${jane_jwt_token}' -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429

You should see the following response, showing that out of the 3 requests, 2 requests were successful while the other was rejected:

200:    2, 429:    1

Finally, send a request with an invalid key:

curl -i "http://127.0.0.1:9080/anything" -H 'Authorization: somewrongkey'

You should see an HTTP/1.1 401 Unauthorized response with the following message:

{"message":"failed to verify jwt"}

You should see the same error message if your token has expired.

Next Steps

You have now learned how to implement basic authentication. APISIX supports other built-in authentication methods, such as key authentication, basic authentication, and HMAC authentication.


API7.ai Logo

API Management for Modern Architectures with Edge, API Gateway, Kubernetes, and Service Mesh.

Product

API7 Cloud

SOC2 Type IIISO 27001HIPAAGDPRRed Herring

Copyright © APISEVEN PTE. LTD 2019 – 2025. Apache, Apache APISIX, APISIX, and associated open source project names are trademarks of the

Apache Software Foundation