can apigee support JWE JSON Serialization in JWT policy

Does Apigee Hybrid’s JWT policy support JWE/JWS JSON Serialization?

While standard JWTs use base64 encoding, some API partners require JWE JSON Serialization. Please clarify if this is a supported feature.

Currently the built-in policies do not support JSON serialization of JWT or JWE. The built-in policies support Compact Serialization only. The documentation is not clear on this. We’ll get that fixed. If you need JSON Serialization, you will need to write your own Java callout for this purpose, or use some other mechanism.

Curious: which partner requires this?

As we are payment company in Thailand, The regulator call our service and require to have JSON serialization for JWE.

Is it possible to add it as a future feature? @dchiesa1

Are you sure about that? I’ve never seen this before. Do you have an English-language version of a document that states this requirement? Does it require the General (IETF RFC 7516 section 7.2.1) or Flattened version (section 7.2.2)? And can you give a specific example showing the JSON JWE format you need to support?

Some more information: Apigee generates and verifies JWT: signed or verified JWT. (And Also Apigee can generate or verify signatures on JWS.) Apigee does not, at the moment, have a built-in policy to generate arbitrary JWE. Per RFC 7519, JWTs are always represented using the JWS Compact Serialization (when the JWT is a signed JWT) or the JWE Compact Serialization (when the JWT is an encrypted JWT).

There’s an outstanding feature request for this. At the moment it has not been prioritized.

So

  1. Apigee does not generate or validate JWE (the general case), with a built-in policy.
  2. Apigee can generate encrypted JWT, but those always use the compact Serialization format, because they are JWT.
  3. You can use an custom policy (like this one) which, despite its name, includes a class that will generate JWE. But again, it generates only in compact format. (EDIT: I just updated that custom policy so that it can generate a JWE in JSON (Flattened) format. Get the version of 20241203 or later.).

The JSON Serialization format for a JWE is pretty straightforward, if (a) you use the flattened format, and (b) you have only one recipient (it is encrypted just once), and (c) it’s ok to use the JWE header as the Additional Authenticated Data (AAD).

This JavaScript policy will convert:

<Javascript name='JS-Convert-JWE-from-Compact-to-JSON-Serialization' timeLimit='200'>
<!--
  // the output format will be something like this:
  // {
  //   "protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
  //   "encrypted_key": "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",
  //   "iv": "AxY8DCtDaGlsbGljb3RoZQ",
  //   "ciphertext": "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
  //   "tag": "Mz-VPPyU4RlcuYv1IwIvzw"
  // }
-->
  <Properties>
    <!-- source: the context variable holding the compact-serialized
         JWE (or encrypted JWT) -->
    <Property name='source'>output_var</Property>
    <!-- destination: the context variable this script will set, to hold the JSON-serialized
         JWE -->
    <Property name='destination'>json-serialized-jwe</Property>
  </Properties>
  <Source><![CDATA[
function convert(jwe) {
  var parts = jwe.split(".");
  if (!parts || !parts.length || parts.length != 5) {
    throw new Error("that is not a compact-serialized JWE");
  }
  var obj = {
    protected: parts[0],
    encrypted_key: parts[1],
    iv : parts[2],
    ciphertext : parts[3],
    tag : parts[4]
  };

  return JSON.stringify(obj, null, 2);
}

var source_jwe = context.getVariable(properties.source);
var converted = convert(source_jwe);
context.setVariable(properties.destination, converted);
]]>
  </Source>
</Javascript>

The Compact Serialization just collects all of those things into a dot-concatenated url-safe string. The JSON form is just more verbose.

the equivalent of the above:

{
  "protected": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJ0eXAiOiJKV1QiLCJlbmMiOiJBMTI4R0NNIn0",
  "encrypted_key": "1Mikpz6E6O9WuXpMiubUZe1ePthQgNEAWW0b7_6TqzZYJavEfUkXGbfkn47lprRXNmQogTz6gs62086LD45JfYLgfBMzu3DkCKIAI3Gr7kTry_u_TVJIsP8IrgDpPK6-kLAbDCle4hVZOjRd-_HnRbx0h9pJxEIEjjf1-_EuWLxoxlXBZzo_uJQIq8kzI7Cv-SR2Cxty9uIXYH-WF0YIl081GDJRzf-zG7eQFgokxq8F0w2VyZiEFBoEXuFET_B5tXVkF-NK6m3RfaqSQWqBB8qgvEAWJ2ouMyQAdQ4AXaxWTH7P4PXf-zEK6xHlAiZsK2HIcnwLwrGHu9ueKD15Vw",
  "iv": "Wvuf6Ze0-Q3-Uzdn",
  "ciphertext": "JNrOWPfLkRYd2vo-qdpVTZhwlWS5-vg2wHbVOoEL4N8DQ_8f6NaCCdmdga80p4tZUnHHwMPTog0iINw54b3E-XMfM524JRyN0Bi23ZM",
  "tag": "8tVIj10iM5uwbTJWcNq3gw"
}

Hi @Teerakiat , welcome to the community—it’s great to have you here!

I noticed that @dchiesa1 provided a response to your question (thanks Dino for engaging). If this resolves your query, feel free to mark it as the accepted solution. Doing so helps others in the community find accurate answers more easily.

Since you’re new, I also invite you to check out our articles and upcoming events for more resources to enhance your experience - Looking forward to seeing you engage more in the community! :blush:

Thanks @dchiesa1 for your solution.