Add additional claims dynamically to GenerateJWT

sujnana
Participant IV

Is it possible to add additional claims dynamically for creating JWT using the out-of-the-box policy? We are getting a claims list from a back end service. This service can return one or more claim lists (claim name and value) like below.

{
  "fid": "a123",
  "userName": "Test user",
  "pEnabled": true,
  "noOfDays": 23
}

Can we add these claims dynamically for GenerateJWT policy? We need each element in the above response as a separate claim in JWT.

Solved Solved
1 5 2,798
1 ACCEPTED SOLUTION

There are multiple ways to do what you want. Maybe one is acceptable.

You can generate a JWT with a known set of claims, for which the values are dynamic. This looks like this:

<GenerateJWT name='GenerateJWT-3'>
  <Algorithm>HS256</Algorithm>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <SecretKey>
    <Value ref='private.secretkey'/>
    <Id>cb24a396-4414-4793-b4ce-90333b694bff</Id>
  </SecretKey>
  <Subject>subject-here</Subject>
  <Issuer>urn://apigee-edge-JWT-policy-demonstration</Issuer>
  <Audience>audience-here</Audience>
  <Id/>  <!-- optional; empty implies generate a jti -->
  <ExpiresIn>8h</ExpiresIn>
  <AdditionalClaims>
    <Claim type='string' name='fid' ref='json.fid'/>
    <Claim type='string' name='userName' ref='json.userName'/>
    <Claim type='boolean' name='pEnabled' ref='json.pEnabled'/>
    <Claim type='number' name='noOfDays' ref='json.noOfDays'/>
  </AdditionalClaims>
  <OutputVariable>output_variable</OutputVariable>
</GenerateJWT>

To use this ^^ approach you would need to shred the json payload you showed into individual variables. That's not hard to do.

The result is a payload like this:

{
    "fid": "a123",
    "sub": "subject-here",
    "aud": "audience-here",
    "iss": "urn://apigee-edge-JWT-policy-demonstration",
    "pEnabled": true,
    "exp": 1529647025,
    "userName": "Test user",
    "iat": 1529618225,
    "jti": "924f5acc-d7a6-43cf-9ac4-45ced838dffc",
    "noOfDays": 23
}

Today, using the GenerateJWT policy, you cannot create a JWT with multiple new "top level" claims that are not known at configuration time. You have to know the claim names. In your case you would have to know the desired claims are fid, noOfDays, userName, and pEnabled.

Today, you CAN use the policy to create a JWT with "nested claims", with properties that are unknown at configuration time, aka dynamic. For example, this configuration:

<GenerateJWT name='GenerateJWT-2'>
  <Algorithm>HS256</Algorithm>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <SecretKey>
    <Value ref='private.secretkey'/>
    <Id>cb24a396-4414-4793-b4ce-90333b694bff</Id>
  </SecretKey>
  <Subject>subject-here</Subject>
  <Issuer>urn://apigee-edge-JWT-policy-demonstration</Issuer>
  <Audience>audience-here</Audience>
  <Id/>  <!-- optional; empty implies generate a jti -->
  <ExpiresIn>8h</ExpiresIn>
  <AdditionalClaims>
    <Claim type='map' name='additional-claim-name' ref='claim_payload'/>
  </AdditionalClaims>
  <OutputVariable>output_variable</OutputVariable>
</GenerateJWT>

...assuming the variable "claim_payload" contains the payload you provided in your question, results in a JWT with a payload like this:

{
    "sub": "subject-here",
    "aud": "audience-here",
    "iss": "urn://apigee-edge-JWT-policy-demonstration",
    "additional-claim-name": {
        "fid": "a123",
        "userName": "Test user",
        "pEnabled": true,
        "noOfDays": 23.0
    },
    "exp": 1529647210,
    "iat": 1529618410,
    "jti": "9545b3f8-89e6-4aba-8a47-2be1650a5178"
}

View solution in original post

5 REPLIES 5

There are multiple ways to do what you want. Maybe one is acceptable.

You can generate a JWT with a known set of claims, for which the values are dynamic. This looks like this:

<GenerateJWT name='GenerateJWT-3'>
  <Algorithm>HS256</Algorithm>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <SecretKey>
    <Value ref='private.secretkey'/>
    <Id>cb24a396-4414-4793-b4ce-90333b694bff</Id>
  </SecretKey>
  <Subject>subject-here</Subject>
  <Issuer>urn://apigee-edge-JWT-policy-demonstration</Issuer>
  <Audience>audience-here</Audience>
  <Id/>  <!-- optional; empty implies generate a jti -->
  <ExpiresIn>8h</ExpiresIn>
  <AdditionalClaims>
    <Claim type='string' name='fid' ref='json.fid'/>
    <Claim type='string' name='userName' ref='json.userName'/>
    <Claim type='boolean' name='pEnabled' ref='json.pEnabled'/>
    <Claim type='number' name='noOfDays' ref='json.noOfDays'/>
  </AdditionalClaims>
  <OutputVariable>output_variable</OutputVariable>
</GenerateJWT>

To use this ^^ approach you would need to shred the json payload you showed into individual variables. That's not hard to do.

The result is a payload like this:

{
    "fid": "a123",
    "sub": "subject-here",
    "aud": "audience-here",
    "iss": "urn://apigee-edge-JWT-policy-demonstration",
    "pEnabled": true,
    "exp": 1529647025,
    "userName": "Test user",
    "iat": 1529618225,
    "jti": "924f5acc-d7a6-43cf-9ac4-45ced838dffc",
    "noOfDays": 23
}

Today, using the GenerateJWT policy, you cannot create a JWT with multiple new "top level" claims that are not known at configuration time. You have to know the claim names. In your case you would have to know the desired claims are fid, noOfDays, userName, and pEnabled.

Today, you CAN use the policy to create a JWT with "nested claims", with properties that are unknown at configuration time, aka dynamic. For example, this configuration:

<GenerateJWT name='GenerateJWT-2'>
  <Algorithm>HS256</Algorithm>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <SecretKey>
    <Value ref='private.secretkey'/>
    <Id>cb24a396-4414-4793-b4ce-90333b694bff</Id>
  </SecretKey>
  <Subject>subject-here</Subject>
  <Issuer>urn://apigee-edge-JWT-policy-demonstration</Issuer>
  <Audience>audience-here</Audience>
  <Id/>  <!-- optional; empty implies generate a jti -->
  <ExpiresIn>8h</ExpiresIn>
  <AdditionalClaims>
    <Claim type='map' name='additional-claim-name' ref='claim_payload'/>
  </AdditionalClaims>
  <OutputVariable>output_variable</OutputVariable>
</GenerateJWT>

...assuming the variable "claim_payload" contains the payload you provided in your question, results in a JWT with a payload like this:

{
    "sub": "subject-here",
    "aud": "audience-here",
    "iss": "urn://apigee-edge-JWT-policy-demonstration",
    "additional-claim-name": {
        "fid": "a123",
        "userName": "Test user",
        "pEnabled": true,
        "noOfDays": 23.0
    },
    "exp": 1529647210,
    "iat": 1529618410,
    "jti": "9545b3f8-89e6-4aba-8a47-2be1650a5178"
}

The nested claim can be used if JWT is generated and used only in Apigee proxies. But our requirement is to send JWT to backend downstream systems (existing) & those systems are expecting JWT claims/headers at top level. I was trying message template for additional claims like below but it didn't work.

<AdditionalClaims>
    {customClaims}
</AdditionalClaims>

Here customClaims is the flow variable that has list of claims xml.

I think, now the solution is to use java callout for JWT creation. I hope Apigee will address this requirement in upcoming releases :).

OK thank you for the feedback. I've raised a ticket (b/110548137) to request the ability to embed "top level" claims in a generated JWT. My guess is that it will not be difficult to implement. Stay tuned.

Is there any outcome of the ticket (b/110548137) to add custom claims at the "top level"?

It's implemented and included in the 190301 cloud release, which is rolling out now. It may not yet be available in your region. Soon.