How to create a signed JWT in IBM Security Verify Access

// This post is about IBM, but is not endorsed or sponsored in any way

There are lots of places in IBM Security Verify Access (ISVA) that you can write custom code. One pattern I see a lot is clients leveraging this ability to make HTTP callouts during user authentication to other microservices in their environments. Often these callouts need to send information in a signed JWT. In this post I’ll detail how you can create a JWT from an ISVA infomap with any arbitrary fields you may want in it.

For example, perhaps you want to send a signed JWT containing specific user data during a call out to an internal API during an Authorization Policy, to retrieve more user data from another system. If the callout is made in an Authorization Policy’s mapping rule, how do you first generate the signed JWT to send? Well…

The short version

In order to create a JWT signed with a certificate, we need to create a short STS Chain. This STS chain will have 2 Modules:

  • STSUU (Validate)
  • JWT (Issue)

When called, the chain will accept an STS Universal User Object, and issue a JWT with the STSUU attributes as the response. Once that’s configured we can create a blank STSUU Object, then any STSUU attributes we set will become fields in the JWT body. To do this part, we’ll create a new mapping rule that creates the STSUU and calls the STS chain using the LocalSTSClient. This mapping rule can either be run as part of an Authentication Policy, or imported and called inside another mapping rule.

Step-by-step

1) Create the new STS Chain template

  • Log in to the ISVA appliance, and navigate to Federation -> Security Token Service.
  • Select ‘Templates’
  • Select ‘Add’ above ‘Templates’ on the left, and fill in the two fields:
  • Select ‘Add’ above ‘Template Contents’ on the right, and add a ‘Default STSUU’ Module in ‘Validate’ mode. This tells the STS chain to validate and ingest an STSUU Object
  • Add a ‘JWT’ Module in ‘Issue’ mode. This tells the STS chain to take the current object and turn it into a JWT
  • Your STS Chain Template should now look like this:
  • Deploy the pending changes

2) Create the STS Chain

Now that we have created a new STS Chain Template, we must create an STS Chain instance that uses this template.

  • Select ‘Module Chains’ at the top of the page
  • Select ‘Add’
  • Fill out the Overview tab, it doesn’t really matter what you put here
  • Fill out the Lookup tab, it does matter what you put here as it’s the identifier we’ll use later in the infomap
  • Configure the security tab as per your requirements. It can be left alone.
  • Configure the JWT Module under properties. Here you can select the certificate for signing or encrypting the JWT
  • Save the Chain
  • Deploy the pending changes

3) Create the infomap

Now we have an STS chain that can generate our signed JWTs for us, we need the infomap code to call it. Navigate to ‘Federation’ -> ‘Mapping Rules’

  • Select ‘Add’
  • Fill out the Mapping Rule name. I’ll use ‘createJWT’
  • Set the type to ‘Infomap’
  • Paste in the code. Below is an example that creates a JWT with two fields. The user’s username, and a static field called ‘testfield’. Customise it as required
  • importClass(Packages.com.tivoli.am.fim.fedmgr2.trust.util.LocalSTSClient);
    importClass(Packages.com.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils);
    importClass(com.tivoli.am.fim.trustserver.sts.uuser.Attribute);
    importPackage(Packages.com.tivoli.am.fim.trustserver.sts);
    
    function createUniversalJWT() {
        //Get context attributes we want to put in the JWT
        var username = context.get(Scope.SESSION, "urn:ibm:security:asf:response:token:attributes", "username");
    
        //Create a blank STSUU to be converted into a JWT by the STS chain, and fill it with stuff
        var stsuu = new STSUniversalUser();
        stsuu.addAttribute(new Attribute("username","urn:ibm:names:ITFIM:5.1:accessmanager",username));
        stsuu.addAttribute(new Attribute("testfield","urn:ibm:names:ITFIM:5.1:accessmanager","testvalue"));
    
        //Convert stsuu into a base token to send to STS chain
        var baseToken = stsuu.toXML().getDocumentElement();
    
        //Call STS chain, extract JWT from response
        var jwt = LocalSTSClient.doRequest("http://schemas.xmlsoap.org/ws/2005/02/trust/Issue", "create_jwt", "create_jwt", baseToken, null);
        jwt = IDMappingExtUtils.extractBinarySecurityToken(jwt.token);
    
        //Put JWT into user's session
        context.set(Scope.SESSION, "urn:ibm:security:asf:response:token:attributes", "tagvalue_jwt", jwt);
    
        //Return the JWT
        return jwt
    }
    
    //Uncomment this line if you are running the mapping rule as part of an authorization policy:
    //createUniversalJWT()
    

In the above example, the JWT is then put into the user’s session context as tagvalue_jwt. You can then configure webseal to send this as a header to the backend if desired.

  • Select ‘Save’
  • Deploy pending changes

4) Bringing it all together

Now we have a mapping rule with a handy function called createUniversalJWT(). When this function is called, it will create a JWT with the custom fields we put into the STSUU Object. There are two main ways to use this:

4.1) Call the function inside another infomap

Say you have another infomap already, and you want the JWT created mid way through, simply import the createJWT mapping rule, and then call the function where required.

	importMappingRule("createJWT");
	....
	....
	....
	var myCustomJWT = createUniversalJWT();
	//You can now use myCustomJWT in an HTTP Callout etc
	...
	...
	...

You could even edit the createJWT infomap such that the createUniversalJWT() function accepts data to include in the JWT.

4.1) As a step in an Authorization Policy

You could also add the infomap as part as an autorization policy, so it runs straight away. This is useful if you want to put the JWT into the user’s session credential, perphaps to then send as a header to the backend. If you do it this way, make sure to uncomment the last infomap line that triggers the createUniversalJWT() function.

Conclusion

Hopefully, if you’ve made it this far through this post then it’s been useful to you! If it was really useful then you might even consider buying me a coffee.