Verify requests containing HS256 or RS256 signed JSON Web Tokens (as specified in RFC 7519). Each of your Consumers will have JWT credentials (public and secret keys) which must be used to sign their JWTs. A token can then be passed through:
- a query string parameter,
- a cookie,
- or HTTP request headers
Kong will either proxy the request to your upstream services if the token’s signature is verified, or discard the request if not. Kong can also perform verifications on some of the registered claims of RFC 7519 (exp and nbf).
Configuration Reference
This plugin is partially compatible with DB-less mode.
In DB-less mode, you configure Kong Gateway declaratively. Therefore, the Admin API is mostly read-only. The only tasks it can perform are all related to handling the declarative config, including:
- Setting a target's health status in the load balancer
- Validating configurations against schemas
- Uploading the declarative configuration using the
/config
endpoint
Consumers and JWT secrets can be created with declarative configuration.
Admin API endpoints which do POST, PUT, PATCH or DELETE on secrets are not available on DB-less mode.
Enable the plugin on a service
Enable the plugin on a route
Enable the plugin globally
A plugin which is not associated to any service, route, or consumer is considered global, and will be run on every request. Read the Plugin Reference and the Plugin Precedence sections for more information.
Parameters
Here's a list of all the parameters which can be used in this plugin's configuration:
Form Parameter | Description |
---|---|
name
required Type: string |
The name of the plugin, in this case jwt . |
service.id
Type: string |
The ID of the Service the plugin targets. |
route.id
Type: string |
The ID of the Route the plugin targets. |
enabled
required Type: boolean Default value: true |
Whether this plugin will be applied. |
config.uri_param_names
optional Default value: jwt
|
A list of querystring parameters that Kong will inspect to retrieve JWTs. |
config.cookie_names
optional |
A list of cookie names that Kong will inspect to retrieve JWTs. |
config.header_names
optional Default value: Authorization
|
A list of HTTP header names that Kong will inspect to retrieve JWTs. |
config.claims_to_verify
optional |
A list of registered claims (according to RFC 7519) that Kong can verify as well. Accepted values: |
config.key_claim_name
optional Default value: iss
|
The name of the claim in which the |
config.secret_is_base64
optional Default value: false
|
If true, the plugin assumes the credential’s |
config.anonymous
optional |
An optional string (consumer uuid) value to use as an “anonymous” consumer if authentication fails. If empty (default), the request will fail with an authentication failure |
config.run_on_preflight
optional Default value: true
|
A boolean value that indicates whether the plugin should run (and try to authenticate) on |
config.maximum_expiration
optional Default value: 0
|
An integer limiting the lifetime of the JWT to |
Documentation
In order to use the plugin, you first need to create a Consumer and associate one or more JWT credentials (holding the public and private keys used to verify the token) to it. The Consumer represents a developer using the final service.
Create a Consumer
You need to associate a credential to an existing Consumer object. A Consumer can have many credentials.
In both cases, the parameters are as described below:
parameter | description |
---|---|
username semi-optional |
The username of the consumer. Either this field or custom_id must be specified. |
custom_id semi-optional |
A custom identifier used to map the consumer to another database. Either this field or username must be specified. |
Create a JWT credential
In both cases the fields/parameters work as follows:
field/parameter | default | description |
---|---|---|
{consumer} |
The id or username property of the Consumer entity to associate the credentials to. |
|
key optional |
A unique string identifying the credential. If left out, it will be auto-generated. | |
algorithm optional |
HS256 |
The algorithm used to verify the token’s signature. Can be HS256 , HS384 , HS512 , RS256 , or ES256 . |
rsa_public_key optional |
If algorithm is RS256 or ES256 , the public key (in PEM format) to use to verify the token’s signature. |
|
secret optional |
If algorithm is HS256 or ES256 , the secret used to sign JWTs for this credential. If left out, will be auto-generated. |
Delete a JWT credential
You can remove a Consumer’s JWT credential by issuing the following HTTP request:
$ curl -X DELETE http://kong:8001/consumers/{consumer}/jwt/{id}
HTTP/1.1 204 No Content
consumer
: Theid
orusername
property of the Consumer entity to associate the credentials to.id
: Theid
of the JWT credential.
List JWT credentials
You can list a Consumer’s JWT credentials by issuing the following HTTP request:
$ curl -X GET http://kong:8001/consumers/{consumer}/jwt
HTTP/1.1 200 OK
consumer
: Theid
orusername
property of the Consumer entity to list credentials for.
{
"data": [
{
"rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK .... -----END PUBLIC KEY-----",
"consumer_id": "39f52333-9741-48a7-9450-495960d91684",
"id": "3239880d-1de5-4dbc-bccf-78f7a4280f33",
"created_at": 1491430568000,
"key": "c5a55906cc244f483226e02bcff2b5e",
"algorithm": "RS256",
"secret": "b0970f7fc9564e65xklfn48930b5d08b1"
}
],
"total": 1
}
Craft a JWT with a secret (HS256)
Now that your Consumer has a credential, and assuming we want to sign it using HS256
, the JWT should be crafted as follows (according to RFC 7519):
First, its header must be:
{
"typ": "JWT",
"alg": "HS256"
}
Secondly, the claims must contain the secret’s key
in the configured claim (from config.key_claim_name
).
That claim is iss
(issuer field) by default. Set its value to our previously created credential’s key
.
The claims may contain other values. Since Kong 0.13.1
, the claim is searched in both the JWT payload and header,
in this order.
{
"iss": "a36c3049b36249a3c9f8891cb127243c"
}
Using the JWT debugger at https://jwt.io with the header (HS256), claims (iss, etc), and secret
associated with this key
(e71829c351aa4242c2719cbfbe671c09), you’ll end up with a JWT token of:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMzZjMzA0OWIzNjI0OWEzYzlmODg5MWNiMTI3MjQzYyIsImV4cCI6MTQ0MjQzMDA1NCwibmJmIjoxNDQyNDI2NDU0LCJpYXQiOjE0NDI0MjY0NTR9.AhumfY35GFLuEEjrOXiaADo7Ae6gt_8VLwX7qffhQN4
Send a request with the JWT
The JWT can now be included in a request to Kong by adding it as a header, if configured in config.header_names
(which contains Authorization
by default):
$ curl http://kong:8000/{route path} \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMzZjMzA0OWIzNjI0OWEzYzlmODg5MWNiMTI3MjQzYyIsImV4cCI6MTQ0MjQzMDA1NCwibmJmIjoxNDQyNDI2NDU0LCJpYXQiOjE0NDI0MjY0NTR9.AhumfY35GFLuEEjrOXiaADo7Ae6gt_8VLwX7qffhQN4'
as a querystring parameter, if configured in config.uri_param_names
(which contains jwt
by default):
$ curl http://kong:8000/{route path}?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMzZjMzA0OWIzNjI0OWEzYzlmODg5MWNiMTI3MjQzYyIsImV4cCI6MTQ0MjQzMDA1NCwibmJmIjoxNDQyNDI2NDU0LCJpYXQiOjE0NDI0MjY0NTR9.AhumfY35GFLuEEjrOXiaADo7Ae6gt_8VLwX7qffhQN4
or as cookie, if the name is configured in config.cookie_names
(which is not enabled by default):
curl --cookie jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMzZjMzA0OWIzNjI0OWEzYzlmODg5MWNiMTI3MjQzYyIsImV4cCI6MTQ0MjQzMDA1NCwibmJmIjoxNDQyNDI2NDU0LCJpYXQiOjE0NDI0MjY0NTR9.AhumfY35GFLuEEjrOXiaADo7Ae6gt_8VLwX7qffhQN4 http://kong:8000/{route path}
The request will be inspected by Kong, whose behavior depends on the validity of the JWT:
request | proxied to upstream service | response status code |
---|---|---|
has no JWT | no | 401 |
missing or invalid iss claim |
no | 401 |
invalid signature | no | 403 |
valid signature | yes | from the upstream service |
valid signature, invalid verified claim optional | no | 401 |
(Optional) Verified claims
Kong can also perform verification on registered claims, as defined in RFC 7519. To perform verification on a claim, add it to the config.claims_to_verify
property:
You can patch an existing jwt plugin:
# This adds verification for both nbf and exp claims:
$ curl -X PATCH http://kong:8001/plugins/{jwt plugin id} \
--data "config.claims_to_verify=exp,nbf"
Supported claims:
claim name | verification |
---|---|
exp |
identifies the expiration time on or after which the JWT must not be accepted for processing. |
nbf |
identifies the time before which the JWT must not be accepted for processing. |
(Optional) Base64 encoded secret
If your secret contains binary data, you can store them as base64 encoded in Kong. Enable this option in the plugin’s configuration:
You can patch an existing Route:
$ curl -X PATCH http://kong:8001/routes/{route id}/plugins/{jwt plugin id} \
--data "config.secret_is_base64=true"
Then, base64 encode your consumers’ secrets:
# secret is: "blob data"
$ curl -X POST http://kong:8001/consumers/{consumer}/jwt \
--data "secret=YmxvYiBkYXRh"
And sign your JWT using the original secret (“blob data”).
Craft a JWT with public/private keys (RS256 or ES256)
If you wish to use RS256 or ES256 to verify your JWTs, then when creating a JWT credential, select RS256
or ES256
as the algorithm
, and explicitly upload the public key in the rsa_public_key
field (including for ES256 signed tokens). For example:
$ curl -X POST http://kong:8001/consumers/{consumer}/jwt \
-F "rsa_public_key=@/path/to/public_key.pem" \
HTTP/1.1 201 Created
{
"consumer_id": "7bce93e1-0a90-489c-c887-d385545f8f4b",
"created_at": 1442426001000,
"id": "bcbfb45d-e391-42bf-c2ed-94e32946753a",
"key": "a36c3049b36249a3c9f8891cb127243c",
"rsa_public_key": "-----BEGIN PUBLIC KEY----- ..."
}
When creating the signature, make sure that the header is:
{
"typ": "JWT",
"alg": "RS256"
}
Secondly, the claims must contain the secret’s key
field (this isn’t your private key used to generate
the token, but just an identifier for this credential) in the configured claim (from config.key_claim_name
).
That claim is iss
(issuer field) by default. Set its value to our previously created credential’s key
.
The claims may contain other values. Since Kong 0.13.1
, the claim is searched in both the JWT payload and header,
in this order.
{
"iss": "a36c3049b36249a3c9f8891cb127243c"
}
Then create the signature using your private keys. Using the JWT debugger at https://jwt.io, set the right header (RS256), the claims (iss, etc), and the associated public key. Then append the resulting value in the Authorization
header, for example:
$ curl http://kong:8000/{route path} \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxM2Q1ODE0NTcyZTc0YTIyYjFhOWEwMDJmMmQxN2MzNyJ9.uNPTnDZXVShFYUSiii78Q-IAfhnc2ExjarZr_WVhGrHHBLweOBJxGJlAKZQEKE4rVd7D6hCtWSkvAAOu7BU34OnlxtQqB8ArGX58xhpIqHtFUkj882JQ9QD6_v2S2Ad-EmEx5402ge71VWEJ0-jyH2WvfxZ_pD90n5AG5rAbYNAIlm2Ew78q4w4GVSivpletUhcv31-U3GROsa7dl8rYMqx6gyo9oIIDcGoMh3bu8su5kQc5SQBFp1CcA5H8sHGfYs-Et5rCU2A6yKbyXtpHrd1Y9oMrZpEfQdgpLae0AfWRf6JutA9SPhst9-5rn4o3cdUmto_TBGqHsFmVyob8VQ'
Generate public/private keys
To create a brand new pair of public/private keys, you can run the following command:
$ openssl genrsa -out private.pem 2048
This private key must be kept secret. To generate a public key corresponding to the private key, execute:
$ openssl rsa -in private.pem -outform PEM -pubout -out public.pem
If you run the commands above, the public key will be written in public.pem
, while the private key will be written in private.pem
.
Using the JWT plugin with Auth0
Auth0 is a popular solution for Authorization, and relies heavily on JWTs. Auth0 relies on RS256, does not base64 encode, and publicly hosts the public key certificate used to sign tokens. Account name is referred to “COMPANYNAME” for the sake of the guide.
To get started, create a Service and a Route that uses that Service. Note: Auth0 does not use base64 encoded secrets.
Create a Service:
$ curl -i -f -X POST http://localhost:8001/services \
--data "name=example-service" \
--data "=http://httpbin.org"
Then create a Route:
$ curl -i -f -X POST http://localhost:8001/routes \
--data "service.id={example-service's id}" \
--data "paths[]=/example_path"
Add the JWT Plugin:
Add the plugin to your Route:
$ curl -X POST http://localhost:8001/route/{route id}/plugins \
--data "name=jwt"
Download your Auth0 account’s X509 Certificate:
$ curl -o {COMPANYNAME}.pem https://{COMPANYNAME}.auth0.com/pem
Extract the public key from the X509 Certificate:
$ openssl x509 -pubkey -noout -in {COMPANYNAME}.pem > pubkey.pem
Create a Consumer with the Auth0 public key:
$ curl -i -X POST http://kong:8001/consumers \
--data "username=<USERNAME>" \
--data "custom_id=<CUSTOM_ID>"
$ curl -i -X POST http://localhost:8001/consumers/{consumer}/jwt \
-F "algorithm=RS256" \
-F "rsa_public_key=@./pubkey.pem" \
-F "key=https://{COMPANYNAME}.auth0.com/" # the `iss` field
The JWT plugin by default validates the key_claim_name
against the iss
field in the token. Keys issued by Auth0 have their iss
field set to
http://{COMPANYNAME}.auth0.com/
. You can use jwt.io to
validate the iss
field for the key
parameter when creating the
Consumer.
Send requests through, only tokens signed by Auth0 will work:
$ curl -i http://localhost:8000 \
-H "Host:example.com" \
-H "Authorization:Bearer "
Upstream Headers
When a JWT is valid, a Consumer has been authenticated, the plugin will append some headers to the request before proxying it to the upstream service, so that you can identify the Consumer in your code:
X-Consumer-ID
, the ID of the Consumer on KongX-Consumer-Custom-ID
, thecustom_id
of the Consumer (if set)X-Consumer-Username
, theusername
of the Consumer (if set)X-Anonymous-Consumer
, will be set totrue
when authentication failed, and the ‘anonymous’ consumer was set instead.
You can use this information on your side to implement additional logic. You can use the X-Consumer-ID
value to query the Kong Admin API and retrieve more information about the Consumer.
Paginate through the JWTs
You can paginate through the JWTs for all Consumers using the following request:
$ curl -X GET http://kong:8001/jwts
{
"total": 3,
"data": [
{
"created_at": 1509593911000,
"id": "381879e5-04a1-4c8a-9517-f85fbf90c3bc",
"algorithm": "HS256",
"key": "UHVwIly5ZxZH7g52E0HRlFkFC09v9yI0",
"secret": "KMWyDsTTcZgqqyOGgRWTDgZtIyWeEtJh",
"consumer": { "id": "3c2c8fc1-7245-4fbb-b48b-e5947e1ce941" }
},
{
"created_at": 1511389527000,
"id": "0dfc969b-02be-42ae-9d98-e04ed1c05850",
"algorithm": "ES256",
"key": "vcc1NlsPfK3N6uU03YdNrDZhzmFF4S19",
"secret": "b65Rs6wvnWPYaCEypNU7FnMOZ4lfMGM7",
"consumer": { "id": "c0d92ba9-8306-482a-b60d-0cfdd2f0e880" }
},
{
"created_at": 1509593912000,
"id": "d10c6f3b-71f1-424e-b1db-366abb783460",
"algorithm": "HS256",
"key": "SqSNfg9ARmPnpycyJSMAc2uR6nxdmc9S",
"secret": "CCh6ZIcwDSOIWacqkkWoJ0FWdZ5eTqrx",
"consumer": { "id": "3c2c8fc1-7245-4fbb-b48b-e5947e1ce941" }
}
]
}
You can filter the list by consumer by using this other path:
$ curl -X GET http://kong:8001/consumers/{username or id}/jwt
{
"total": 1,
"data": [
{
"created_at": 1511389527000,
"id": "0dfc969b-02be-42ae-9d98-e04ed1c05850",
"algorithm": "ES256",
"key": "vcc1NlsPfK3N6uU03YdNrDZhzmFF4S19",
"secret": "b65Rs6wvnWPYaCEypNU7FnMOZ4lfMGM7",
"consumer": { "id": "c0d92ba9-8306-482a-b60d-0cfdd2f0e880" }
}
]
}
username or id
: The username or id of the consumer whose jwts need to be listed
Retrieve the Consumer associated with a JWT
It is possible to retrieve a Consumer associated with a JWT using the following request:
curl -X GET http://kong:8001/jwts/{key or id}/consumer
{
"created_at":1507936639000,
"username":"foo",
"id":"c0d92ba9-8306-482a-b60d-0cfdd2f0e880"
}
key or id
: The id
or key
property of the JWT for which to get the
associated Consumer.