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 addexp
ornbf
in UNIX timestamp.
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.