DecodeJWT and GenerateJWT compatibility

Huw
Bronze 4
Bronze 4

Hi, wondering if I really need a JavaScript policy for what I'm doing. I'm taking some claims from one JWT and put them into another, but having issues with claims that are an array.

The JWT payload looks like something this 

 

{ 
"sub": "user@example.com",
"roles": ["role1","role2"]
}

 

I'm using the DecodeJWT policy to get make the claims available:

 

<DecodeJWT name="GetClaims">
    <Source>source-jwt</Source>
</DecodeJWT>

 

So, my first attempt was to use the outputs from DecodeJWT to pass the "roles" claim across to a GenerateJWT policy and create the new JWT:

 

<GenerateJWT name="CreateNewJwt">
    <!-- other settings omitted for brevity -->
    <AdditionalClaims>
        <Claim name="roles" array="true" ref="jwt.GetClaims.decoded.claim.roles" />
    </AdditionalClaims>
    <OutputVariable>my-new-token</OutputVariable>
</GenerateJWT>

 

However, it seems that even though DecodeJWT outputs the array in JSON notation, GenerateJWT expects the array to be provided as a comma separate list with no quotes. i.e. 

  • DecodeJWT outputs the roles claim as: ["role1","role2"]
  • GenerateJWT needs me to pass it as: role,role2

In the end I got it working with a JavaScript policy in between the two, but it feels strange that I need to do that... am I missing something in the docs?

 

Solved Solved
2 3 151
1 ACCEPTED SOLUTION

Huw
Bronze 4
Bronze 4

As it seems there is indeed a need for a workaround, I thought it might be helpful to share the JavaScript snippet I ended up using.

So the flow steps look like this:

 

<Step><Name>DecodeJWT-OriginalToken</Name></Step>
<Step><Name>JavaScript-MapClaimsArrayToCsv</Name></Step>
<Step><Name>GenerateJWT-NewToken</Name></Step>

 

 The JavaScript policy looks like this:

 

<Javascript name="JavaScript-MapClaimsArrayToCsv">
    <Properties>
        <Property name="sourceVariable">jwt.DecodeJWT-OriginalToken.decoded.claim.roles</Property>
        <Property name="targetVariable">main.original-token.claims.roles</Property>
    </Properties>
    <ResourceURL>jsc://JsonArrayToCsv.js</ResourceURL>
</Javascript>

 

And the JsonArrayToCsv.js JavaScript file looks like this:

 

var array = JSON.parse(context.getVariable(properties.sourceVariable));
context.setVariable(properties.targetVariable, array.join(','));

 

(the JavaScript could probably do with a little more error handling in case the claim is not array, but in my situation I'm 99.99% sure it will be)

And then the GenerateJWT policy will look something like this:

 

<GenerateJWT "GenerateJWT-NewToken">
    <!--other elements omitted for brevity -->
    <AdditionalClaims>
        <Claim name="roles" array="true" ref="main.original-token.claims.roles" />
    </AdditionalClaims>
</GenerateJWT>

 

 

View solution in original post

3 REPLIES 3

I too would have thought, that that would work.  It seems like that should be easier to do. 

 

Huw
Bronze 4
Bronze 4

As it seems there is indeed a need for a workaround, I thought it might be helpful to share the JavaScript snippet I ended up using.

So the flow steps look like this:

 

<Step><Name>DecodeJWT-OriginalToken</Name></Step>
<Step><Name>JavaScript-MapClaimsArrayToCsv</Name></Step>
<Step><Name>GenerateJWT-NewToken</Name></Step>

 

 The JavaScript policy looks like this:

 

<Javascript name="JavaScript-MapClaimsArrayToCsv">
    <Properties>
        <Property name="sourceVariable">jwt.DecodeJWT-OriginalToken.decoded.claim.roles</Property>
        <Property name="targetVariable">main.original-token.claims.roles</Property>
    </Properties>
    <ResourceURL>jsc://JsonArrayToCsv.js</ResourceURL>
</Javascript>

 

And the JsonArrayToCsv.js JavaScript file looks like this:

 

var array = JSON.parse(context.getVariable(properties.sourceVariable));
context.setVariable(properties.targetVariable, array.join(','));

 

(the JavaScript could probably do with a little more error handling in case the claim is not array, but in my situation I'm 99.99% sure it will be)

And then the GenerateJWT policy will look something like this:

 

<GenerateJWT "GenerateJWT-NewToken">
    <!--other elements omitted for brevity -->
    <AdditionalClaims>
        <Claim name="roles" array="true" ref="main.original-token.claims.roles" />
    </AdditionalClaims>
</GenerateJWT>

 

 

sounds great! Thanks for contributing. 

One note - for very small JS snips, you may wish to embed them directly in the policy file.  Something like this: 

<Javascript name="JavaScript-MapClaimsArrayToCsv">
    <Properties>
        <Property name="sourceVariable">jwt.DecodeJWT-OriginalToken.decoded.claim.roles</Property>
        <Property name="targetVariable">main.original-token.claims.roles</Property>
    </Properties>
    <Source>
var array = JSON.parse(context.getVariable(properties.sourceVariable));
context.setVariable(properties.targetVariable, array.join(','));
    </Source>
</Javascript>

In which case you don't need the ResourceURL or the separate jsc resource file. 

And, if you're doing that sort of thing, the properties may or may not be helpful.  So you might refactor to something like this: 

<Javascript name="JavaScript-MapClaimsArrayToCsv">
    <Source>
var array = JSON.parse(context.getVariable('jwt.DecodeJWT-OriginalToken.decoded.claim.roles'));
context.setVariable('main.original-token.claims.roles', array.join(','));
    </Source>
</Javascript>

If you happen to need to use XML-sensitive characters in your JS, like ampersand (&) or angle brackets, then you need to embed the contents of the Source into a cdata section, like so: 

<Javascript name="JavaScript-MapClaimsArrayToCsv">
  <Source>
    <![CDATA[
var array = JSON.parse(context.getVariable('jwt.DecodeJWT-OriginalToken.decoded.claim.roles'));
context.setVariable('main.original-token.claims.roles', array.join(','));
    ]]>
    </Source>
</Javascript>