Assigning nested attributes with special characters in Assign Message policy

I have the below AssignMessage policy which comes after a VerifyJWT token and I'm trying to inject some of the claims into the request headers. While I was able to do it with most of the claims, one of the claims attributes look something like { "https://api.abc.com/roles": ["org-admin"] }, and it appeared as "Roles": "jwt.Verify-JWT-RS256.claim[\"https:\/\/api.abc.com\/roles\"]" in my headers.

Is it possible / how can I escape the special characters? I have tried the below:
<Header name="roles">{jwt.Verify-JWT-RS256.claim.https://api.abc.com/roles}</Header>

<Header name="roles">{jwt.Verify-JWT-RS256.claim["https:\/\/api.abc.com\/roles"]}</Header>

<Header name="roles">{jwt.Verify-JWT-RS256.claim[https://api.abc.com/roles]}</Header>

and other variations but nothing worked..

AssignMessage policy:

<?xml version="1.0" encoding="UTF-8"?>
<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-1">
   <DisplayName>Assign Message-1</DisplayName>
   <Properties />
   <Add>
      <Headers>
         <Header name="scopes">{jwt.Verify-JWT-RS256.claim.permissions}</Header>
         <Header name="roles">{jwt.Verify-JWT-RS256.claim["https:\/\/api.abc.com\/roles"]}</Header>
      </Headers>
   </Add>
   <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
   <AssignTo createNew="false" transport="http" type="request" />
</AssignMessage>

 

Solved Solved
0 7 457
2 ACCEPTED SOLUTIONS

I'm assuming the JWT payload looks something like this:

 

{
  "iss": "42461AEB-4FB3-4C98-AB3B-0628E5C7473C",
  "sub": "example-subject@example.com",
  "iat": 1648843205,
  "https://api.abc.com/roles": [
    "reader"
  ],
  "scope": "abc def",
  "exp": 1648846805
}

 

With that kind of claim name, you cannot use the curly braces within the Header element in AssignMessage to directly reference the "roles" claim.

For variables with a name that contains slashes, you need to reference them indirectly, by using an intermediate variable. Like this:

 

<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-1">

  <AssignVariable>
    <Name>decoded-roles</Name>
    <Ref>jwt.Verify-JWT-RS256.decoded.claim.https://api.abc.com/roles</Ref>
  </AssignVariable>

   <Add>
      <Headers>
         <Header name="scopes">{jwt.Verify-JWT-RS256.decoded.claim.permissions}</Header>
         <Header name="roles">{decoded-roles}</Header>
      </Headers>
   </Add>

   <!-- this should be false if you want to be conservative -->
   <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>

   <!-- AssignTo is unnecessary, unless you assign to something other than request -->
   <!--
   <AssignTo createNew="false" transport="http" type="request" />
   --> 
</AssignMessage>

 

View solution in original post

In JavaScript you can do this: 

<Javascript name="JavaScript-1">
    <Properties>
      <Property name="jwtRolesVariableName">jwt.Verify-JWT-RS256.claim.https://api.abc.com/roles</Property>
    </Properties>
  <ResourceURL>jsc://JavaScript-1.js</ResourceURL>
</Javascript>

And then reference that property in the JS itself: 

var roles = context.getVariable(properties.jwtRolesVariableName); 
...

 

View solution in original post

7 REPLIES 7

I'm assuming the JWT payload looks something like this:

 

{
  "iss": "42461AEB-4FB3-4C98-AB3B-0628E5C7473C",
  "sub": "example-subject@example.com",
  "iat": 1648843205,
  "https://api.abc.com/roles": [
    "reader"
  ],
  "scope": "abc def",
  "exp": 1648846805
}

 

With that kind of claim name, you cannot use the curly braces within the Header element in AssignMessage to directly reference the "roles" claim.

For variables with a name that contains slashes, you need to reference them indirectly, by using an intermediate variable. Like this:

 

<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-1">

  <AssignVariable>
    <Name>decoded-roles</Name>
    <Ref>jwt.Verify-JWT-RS256.decoded.claim.https://api.abc.com/roles</Ref>
  </AssignVariable>

   <Add>
      <Headers>
         <Header name="scopes">{jwt.Verify-JWT-RS256.decoded.claim.permissions}</Header>
         <Header name="roles">{decoded-roles}</Header>
      </Headers>
   </Add>

   <!-- this should be false if you want to be conservative -->
   <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>

   <!-- AssignTo is unnecessary, unless you assign to something other than request -->
   <!--
   <AssignTo createNew="false" transport="http" type="request" />
   --> 
</AssignMessage>

 

Thank you! Would like to check if this is similar if I use the JavaScript Policy? As there were some string manipulations that I wanted to do using Javascript, I thought it would be easier to just use Javascript Policy to assign message as well. However, when I tried to do:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Javascript continueOnError="true" enabled="true" timeLimit="200" name="JavaScript-1">
    <DisplayName>JavaScript-1</DisplayName>
    <AssignVariable>
      <Name>jwtRoles</Name>
      <Ref>jwt.Verify-JWT-RS256.claim.https://api.abc.com/roles</Ref>
    </AssignVariable>
    <Properties>
      <Property name="jwtRoles">{jwtRoles}</Property>
    </Properties>
  <ResourceURL>jsc://JavaScript-1.js</ResourceURL>
</Javascript>

and tried to access it in the js file and set it on the request header, it was empty `var roles = context.getVariable('jwtRoles'); context.setVariable("request.header.roles", roles);`

You cannot use AssignVariable in the javascript policy xml. AssignVariable tag is for AssignMessage policy.

1. You can either use the AssignMessage policy then a JavaScript policy

2. Use only Javascript policy and have your javascript use context.getVariable to retrieve what you're providing as the reference variable in the AssignMessage

3. Depending on the string manipulations you're trying to perform, you may not need javascript - a number of different helpers are available via message template functions that you could then use in an AssignMessage policy directly.

In JavaScript you can do this: 

<Javascript name="JavaScript-1">
    <Properties>
      <Property name="jwtRolesVariableName">jwt.Verify-JWT-RS256.claim.https://api.abc.com/roles</Property>
    </Properties>
  <ResourceURL>jsc://JavaScript-1.js</ResourceURL>
</Javascript>

And then reference that property in the JS itself: 

var roles = context.getVariable(properties.jwtRolesVariableName); 
...

 

I thought there was a typo and properties.jwtRolesVariableName should be quotes, but I tried it and your syntax is correct it shouldn't be in quotes. I'm just wondering whether there's some reference in the documentation for how variables work in the flow - its not obvious to me how we should reference properties without quotes, but some other variables with quotes like so:
var roles = context.getVariable(properties.jwtRoles);
var scope = context.getVariable('jwt.Verify-JWT-RS256.claim.permissions');

I can see this doc: https://docs.apigee.com/api-platform/fundamentals/introduction-flow-variables , but it kind of suggests that there are only 4 main variables: request, response, system and target?

Within the context of the JavaScript, the identifier named "properties" resolves to a hash of all the properties you have configured in the policy XML. Therefore if this is your XML:

 

<Javascript name='JS-1'>
  <Properties>
    <Property name="foobar">any-value-here</Property>
    <Property name="another-name-here">Blue42</Property>
  </Properties>
  ...

 

...then within the JS context, properties['foobar'] will hold a string containing "any-value-here", and properties['another-name-here'] will contain "Blue42". Because of a peculiarity with JavaScript the language, you can also use the properties.foobar syntax for the former. Because another-name-here is not a valid identifier name in JavaScript (identifiers must not contain dashes), you need to use the quoted version of that name to retrieve that property value. This is just how hashes or objects work in JavaScript.

The context.getVariable() call requires a single parameter, a string, which is the name of a context variable that has been set by some previous policy or execution step. So you must pass a string. You can hard quote the string, like this: 

// example 1
var scope = context.getVariable('jwt.Verify-JWT-RS256.decoded.claim.https://a.b.com/scopes');

or you can pass a variable as that parameter, like this:  

// example 2
var varname = 'jwt.Verify-JWT-RS256.decoded.claim.https://a.b.com/scopes';
var scope = context.getVariable(varname);

or you can refer to a named property, that has been implicitly set by the XML, like this: 

// example 3
var scope = context.getVariable(properties.scopesVar);

This last example requires that you have a Property element in the JS XML, and it defines nameOfProperty.  Like this: 

<Javascript name='JS-1'>
  <Properties>
    <Property name="scopesVar">jwt.Verify-JWT-RS256.decoded.claim.https://a.b.com/scopes</Property>
  </Properties>
  <ResourceURL>jsc://my-js-code.js</ResourceURL>
</Javascript>

Specifying the thing in the XML might seem weird - why put it there?  It's mostly useful for externalizing some of the information used by the JavaScript code. For example by externalizing the name of the context variable, I can use the same JS code with different VerifyJWT policies. ( different policies set different context variables)  So I could have a 2nd JS policy that references the same javascript code, but parameterizes it differently, like this: 

<Javascript name='JS-2'>
  <Properties>
    <Property name="scopesVar">jwt.VJWT-2.decoded.claim.scopes</Property>
  </Properties>
  <ResourceURL>jsc://my-js-code.js</ResourceURL>
</Javascript>

That would work with a differently-shaped input JWT.  

I hope this is helping to clarify. 

If you don't want to re-use the JS, then omit the whole properties thing, and just hard-code the name of the scopes variable in the JS code. AND Quote it!  As in example 1 above.

That was very helpful, thank you!