Thanks for that. what I understand is:
- you’re using Apigee GenerateJWT to generate a JWT
- one of the claims is created via an AdditionalClaims/Claim element, using a variable reference
- you want the API proxy to send the output of that (the generated JWT) to the backend (upstream) system.
- The people that run the upstream system are saying that the JWT they receive is “broken” - I guess because the forward-slashes are being backslash-escaped.
I just tested GenerateJWT with your configuration and a variable that holds “a/b/c/d” and I see that the base64-decoded payload looks like this:
{"aud":["audience1","audience2"],"iss":"urn:\/\/apigee-edge-JWT-policy-test","exp":1590643673,"actions":"a\/b\/c\/b","iat":1590614873,"jti":"a264a537-32cd-4fd1-9cda-507f96566297"}
…which is consistent with my understanding of your description.
It’s unfortunate, but: Escapes of forward slash are valid though not required in JSON.
A correct processor of JSON must be able to handle backslash-escaped forward slashes. Since the JWT payload is simply JSON, While you might look at the behavior of Apigee GenerateJWT here and say “I don’t want the escapes” , it is correct and valid. The way to avoid the problem is: the backend needs to handle the escaped JSON properly.
One possible workaround is to use the GenerateJWS policy. The GenerateJWS policy signs a payload, similar to the way the GenerateJWT signs a payload. But, the payload in a JWS can be “anything”, any stream of bytes. The GenerateJWS policy doesn’t assume you want a JSON payload and doesn’t quietly insert backslash-escapes for forward-slashes if the payload happens to be JSON.
The policy configuration would look like this:
<GenerateJWS name='GenerateJWS-1'>
<Algorithm>HS256</Algorithm>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<SecretKey>
<Value ref='private.secretkey'/>
<Id>cb24a396-4414-4793-b4ce-90333b694bff</Id>
</SecretKey>
<Payload ref='jws-payload'/>
<AdditionalHeaders>
<Claim name='typ'>JWT</Claim>
</AdditionalHeaders>
<OutputVariable>output-jwt</OutputVariable>
</GenerateJWS>
And before that, you would need to set into the variable “jws-payload”, the exact JSON you want to sign. You could use something like this:
<AssignMessage name='AM-Payload'>
<AssignVariable>
<Name>jws-payload</Name>
<Value>{"aud":["audience1","audience2"],"iss":"urn://apigee-edge-JWT-policy-test","exp":1590643673,"actions":"a/b/c/b","iat":1590614873,"jti":"a264a537-32cd-4fd1-9cda-507f96566297"}</Value>
</AssignVariable>
</AssignMessage>
Be aware: the iat and exp claims are fixed in this example, and that’s probably not what you want. So a better method may be to use a JS step to create the payload and initialize the iat/exp claims as you prefer. Maybe like this:
<Javascript name='JS-SetPayload' >
<Source>
var payload = {
"aud":["audience1","audience2"],
"iss":"urn://apigee-edge-JWT-policy-test",
"actions":"a/b/c/b"
};
var lifetimeInSeconds = 600;
var now = new Date();
now = Math.round(now.valueOf()/1000);
payload.iat = now
payload.exp = now + lifetimeInSeconds;
context.setVariable('jws-payload',JSON.stringify(payload));
</Source>
</Javascript>