The jwt-auth plugin authenticates and authorizes requests based on JSON Web Tokens (JWT). It parses a JWT from URL parameters, request headers, or cookies and verifies the token to grant or deny access. Unlike JWT authentication and authorization, this plugin also identifies callers, allowing you to configure different JWT credentials for each caller.
Configuration fields
Authentication configuration
|
Name |
Data type |
Required |
Default value |
Description |
|
consumers |
array of object |
Required |
- |
The service consumers used for request authentication. |
|
global_auth |
Array of strings |
Optional (instance-level configuration only) |
- |
Can only be configured at the instance level. If set to true, authentication applies globally. If set to false, authentication applies only to configured domain names and routes. If not configured, authentication applies globally only when no domain name or route configurations exist, maintaining backward compatibility. |
The following table describes the fields for each item in consumers.
|
Name |
Data type |
Required |
Default value |
Description |
|
name |
string |
Required |
- |
The name of the consumer. |
|
jwks |
string |
Optional. `jwks` or `remote_jwks` is required. |
- |
A JSON Web Key Set (JWKS) string as specified by JSON Web Key (JWK), containing the public key or symmetric key used to verify the JWT signature. |
|
remote_jwks |
object |
Optional. Choose either `jwks` or `remote_jwks`. |
{"uri":"http://127.0.0.1/keys","service":"test.static","port":"80","ttl":30000,"timeout":3000} |
Periodically pulls the JWKS from a specified service URI. If both `jwks` and `remote_jwks` are configured, the remotely pulled JWKS takes precedence. Note
The size of the JWKS returned by `remote_jwks` should be less than 1 MB. If the size exceeds this limit and an error occurs, join DingTalk group 88010006189 or submit a ticket for support. |
|
issuer |
string |
Optional |
- |
The issuer of the JWT. This must match the `iss` field in the payload. |
|
claims |
object |
Optional |
- |
The key-value pairs that correspond to the key-value pairs in the payload, used to verify that multiple fields match the payload. For example, if you configure `aud: mobile-site`, the `aud` field in the payload must be `mobile-site`. |
|
claims_to_headers |
array of object |
Optional |
- |
Extracts specified fields from the JWT payload and sets them as request headers before forwarding to the backend. |
|
from_headers |
array of object |
Optional |
[{"name":"Authorization","value_prefix":"Bearer"}] |
Extracts the JWT from the specified request headers. |
|
from_params |
array of string |
Optional |
access_token |
Extracts the JWT from the specified URL parameters. |
|
from_cookies |
array of string |
Optional |
- |
Extracts the JWT from the specified cookies. |
|
clock_skew_seconds |
number |
Optional |
60 |
The allowed clock skew in seconds when verifying the `exp` and `iat` fields of the JWT. |
|
keep_token |
bool |
Optional |
true |
Whether to keep the JWT when forwarding the request to the backend. |
The default values are used only when from_headers, from_params, and from_cookies are not configured.
-
The following table describes the fields for each item in
from_headers.Name
Data type
Required
Default value
Description
name
string
Required
-
The request header from which the JWT is extracted.
value_prefix
string
required
-
The prefix to remove from the header value. The remaining part is treated as the JWT.
-
The following table describes the fields for each item in
claims_to_headers.Name
Data type
Required
Default value
Description
claim
string
Required
-
A specified field in the JWT payload. The value must be a string or unsigned integer.
header
string
Required
-
The request header to populate with the extracted claim value and forward to the backend.
override
bool
Optional
true
-
If true, overwrites the request header if it has the same name.
-
If false, appends the value as a duplicate request header.
-
-
The following table describes the fields for each item in
remote_jwks.Name
Data type
Required
Default value
Description
uri
string
Required
-
The request URL.
service
string
Required
-
-
Example for a Kubernetes service: foo.default.svc.cluster.local.
-
Example for a Nacos service: foo.DEFAULT-GROUP.public.nacos.
-
For a DNS service named test, enter test.dns.
-
For a static IP service named test, enter test.static.
port
bool
Required
-
The service port.
timeout
bool
Optional
3000
The service request timeout in milliseconds.
ttl
bool
Optional
30000
The cache duration in milliseconds.
-
Authorization configuration (optional)
|
Name |
Data type |
Required |
Default value |
Description |
|
allow |
array of string |
Optional (not for instance-level configuration) |
- |
Can only be configured on fine-grained rules such as routes or domain names. Specifies which consumers are allowed to access the matched resource. |
-
Authorization and authentication configurations cannot be used in the same rule.
-
For requests that pass authentication and authorization, an
X-Mse-Consumerheader is added to the request to identify the caller's name.
Configuration examples
Configure global authentication and route-level authorization
This example enables global JWT authentication at the instance level and restricts route-level access to specific consumers.
If a JWT can match multiple JWKS, the first matching consumer in the configuration order is used.
Plugin configuration
Configure the plugin at the instance level as follows:
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
Route and domain name configuration
For the route-a and route-b routes, configure the plugin as follows:
allow:
- consumer1
For the *.example.com and test.com domain names, configure the plugin as follows:
allow:
- consumer2
-
In this example,
route-aandroute-bare the route names entered when you create the gateway routes. When a request matches these routes, callers with the nameconsumer1are allowed access. Other callers are denied access. -
In this example,
*.example.comandtest.comare used to match the request's domain name. When the domain name matches, callers with the nameconsumer2are allowed access. Other callers are denied access.
Request examples
The following requests are allowed under this configuration. These examples assume the requests match the `route-a` route.
-
Set the JWT in a URL parameter.
curl 'http://xxx.hello.com/test?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEy****.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4' -
Set the JWT in an HTTP request header.
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
Verify the result
After successful authentication, the X-Mse-Consumer header is added to the request with the caller's name. In this example, its value is consumer1.
The following requests will be denied.
-
The request does not provide a JWT, so a 401 error is returned.
-
The caller matched by the provided JWT does not have access permission, so a 403 error is returned.
Enable at the gateway instance level but disable for a specific route
Configure the plugin at the instance level as follows:
global_auth: true
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
For the route-b route, configure the plugin as follows:
_disable_: true
In this example, route-b is the route name entered when you create the gateway route. Because _disable_ is set to true, when a request matches this route, the plugin is disabled. No JWT authentication is performed, and all users are allowed access.
For requests that do not match the route-b route, JWT authentication is performed because global_auth is set to true at the instance level. Both consumer1 and consumer2 are allowed access.
Enable at the domain name level but disable for a specific route
Configure the plugin at the instance level as follows:
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
For the route-b route, configure the plugin as follows:
_disable_: true
For the *.example.com domain name, configure the plugin as follows:
allow:
- consumer1
- consumer2
In this example, route-b is the route name entered when you create the gateway route. Because _disable_ is set to true, when a request matches this route, the plugin is disabled. No JWT authentication is performed, and all users are allowed access.
In this example, *.example.com is used to match the request's domain name. When the domain name matches, callers with the name consumer1 or consumer2 are allowed access. Other callers are denied access.
Rules are matched sequentially: the first match is applied and no further rules are evaluated. Route rules take priority over domain name rules, so you can enable the plugin for a domain name but disable it for a specific route under that domain.
Related error codes
|
HTTP status code |
Error message |
Cause |
|
401 |
Jwt missing |
The request header does not provide a JWT. |
|
401 |
Jwt expired |
The JWT has expired. |
|
401 |
Jwt verification fails |
JWT payload validation failed. For example, the `iss` does not match. |
|
403 |
Access Denied |
No permission to access the current route. |