Skip to main content
Version: Next

jwe-decrypt

Description#

The jwe-decrypt Plugin reads a five-part compact token from a request header, selects a Consumer by the token's kid, decrypts the ciphertext with AES-256-GCM, and writes the plaintext to a configured header before proxying the request. You can enable the Plugin on APISIX Routes or Services.

The token uses JWE Compact Serialization with the dir key management algorithm and the A256GCM content encryption algorithm, so a token produced by a standard JWE library is accepted. Configure a 32-byte decryption secret on the Consumer.

warning

For backward compatibility, the Plugin also accepts a token whose ciphertext was encrypted without the protected header as AES-GCM additional authenticated data (AAD), which is how APISIX itself used to generate them. The header of such a token, including its kid, is not authenticated. Use a trusted token generator, and prefer a JWE library that follows RFC 7516 so that the header is covered by the AAD.

caution

The decrypted plaintext is forwarded in a request header. For sensitive plaintext, do not rely on an HTTPS Upstream alone: APISIX does not verify server certificates for standard HTTP Upstreams. Send the request over an authenticated, protected network path, such as through a proxy or service mesh that validates the upstream server's identity. Restrict access to the upstream and avoid logging the configured forwarding header.

Attributes#

Consumer#

NameTypeRequiredDefaultValid valuesDescription
keystringTrueA unique key that identifies the Credential for a Consumer.
secretstringTrue32 bytesA shared symmetric key. Use a secret reference, such as $env://... or $secret://....
is_base64_encodedbooleanFalsefalseSet to true if the secret is base64url encoded. The decoded secret must still be 32 bytes.

Route or Service#

NameTypeRequiredDefaultValid valuesDescription
headerstringTrueAuthorizationThe header to get the token from.
forward_headerstringTrueAuthorizationName of the header that passes the plaintext to the Upstream.
strictbooleanFalsetrueIf true, return a 403 error when the JWE token is missing. If false, continue when the token is not found.

Examples#

The examples below demonstrate how you can work with the jwe-decrypt Plugin for different scenarios.

note

You can fetch the admin_key from config.yaml and save to an environment variable with the following command:

admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')

Create a Consumer with the Decryption Key#

The following example demonstrates how to create a Consumer with the decryption key and generate a JWE token for it.

Create a Consumer with jwe-decrypt and configure the decryption key:

To generate a JWE token for the Consumer, use any JWE library that supports direct encryption with A256GCM, with the Consumer secret as the key. The token structure is:

base64url(header).<empty>.base64url(iv).base64url(ciphertext).base64url(tag)

where the header is {"alg":"dir","enc":"A256GCM","kid":"<consumer-key>"}; alg and enc are rejected if they are set to anything else. The IV must be unique and randomly generated for every token; never reuse an IV with the same key.

As RFC 7516 requires, a JWE library authenticates the encoded protected header as the AES-GCM additional authenticated data (AAD), which makes the kid tamper-proof. Tokens encrypted without AAD, such as the ones APISIX itself used to generate, are still accepted for backward compatibility.

For example, the following token encrypts the payload {"uid":10000,"uname":"test"} for the Consumer key jack-key with the secret configured above:

eyJraWQiOiJqYWNrLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..vi29KBCQKcVmPwTT.VToyPMFbq-ZY05MIpntP1N3AmYeq3zELQ0B6iQ.vuTPG2ODc-DjUTjNCzfA2A

Decrypt Data with JWE#

The following example demonstrates how to decrypt the JWE token generated above.

Create a Route with jwe-decrypt to decrypt the authorization header:

Send a request to the Route with the JWE encrypted data in the Authorization header:

curl "http://127.0.0.1:9080/anything/jwe" -H 'Authorization: eyJraWQiOiJqYWNrLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..vi29KBCQKcVmPwTT.VToyPMFbq-ZY05MIpntP1N3AmYeq3zELQ0B6iQ.vuTPG2ODc-DjUTjNCzfA2A'

You should see a response similar to the following, where the Authorization header shows the plaintext of the payload:

{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Authorization": "{\"uid\":10000,\"uname\":\"test\"}",
"Host": "127.0.0.1",
"User-Agent": "curl/8.1.2",
"X-Amzn-Trace-Id": "Root=1-6510f2c3-1586ec011a22b5094dbe1896",
"X-Forwarded-Host": "127.0.0.1"
},
"json": null,
"method": "GET",
"origin": "127.0.0.1, 119.143.79.94",
"url": "http://127.0.0.1/anything/jwe"
}