Consuming JWT userinfo response

Consuming JWT userinfo response as IBM Security Verify Access relying party

IBM Security Verify Access (ISVA) Federation module acts as a OpenID Relying party, it can consume only JSON format userinfo response.

This article goes through steps required for a ISVA Relying party to consume a JWT format userinfo response.

Pre-requisites

  • IBM Security Verify Access configured as OIDC Relying party.

Details required from the OpenID Provider

  • .well-known endpoint or userinfo endpoint
  • JWT Signing Algorithm
  • JWKS URL
  • JWT Encryption Algorithm and Key Transport Algorithm

Verify Access Configuration

  1. Login as admin user to the Verify Access local management interface.

  2. Navigate to Federation -> Secure Token Service [block:image]
    {
    "images": [
    {
    "image": [
    "https://files.readme.io/2b7b063-STS.png",
    "2b7b063-STS.png",
    1648,
    700,
    "#000000",
    null,
    "660cc96efb8607001f0030ad"
    ]
    }
    ]
    }
    [/block]
    .

    * Configure a Template with Default JWT Module (Validate Mode) and Default STSUU(Issue Mode).
    
    [block:image]
    

    {
    "images": [
    {
    "image": [
    "https://files.readme.io/7d848b7-STS2.png",
    "7d848b7-STS2.png",
    1648,
    700,
    "#000000",
    null,
    "660cc96e8b8b9d0063b52b33"
    ]
    }
    ]
    }
    [/block]

    * Configure an STS Chain with the template created in the previous step. Configure the Lookup tab and Properties tab with JWT Signing and JWT Encryption settings.
    
    [block:image]
    

    {
    "images": [
    {
    "image": [
    "https://files.readme.io/74d0bb5-STS5.png",
    "74d0bb5-STS5.png",
    1648,
    700,
    "#000000",
    null,
    "660cc96f970ffb0059ea5fe7"
    ]
    }
    ]
    }
    [/block]

    1648 1648
    * Save and deploy the pending changes.
    
  3. Navigate to Federation -> Mapping rule. Edit the OIDCRP mapping rule and copy the following snippet.

    importPackage(Packages.com.tivoli.am.fim.trustserver.sts);
    importPackage(Packages.com.tivoli.am.fim.trustserver.sts.uuser);
    importPackage(Packages.com.tivoli.am.fim.trustserver.sts.utilities);
    importPackage(Packages.com.ibm.security.access.httpclient);
    importClass(Packages.com.tivoli.am.fim.fedmgr2.trust.util.LocalSTSClient);
    IDMappingExtUtils.traceString("oidc_rp mapping rule called with stsuu: " + stsuu.toString());
    
    /*
    * Construct a basic identity made up of iss and sub
    */ 
    
    var iss = stsuu.getAttributeContainer().getAttributeValueByName("iss");
    var sub = stsuu.getAttributeContainer().getAttributeValueByName("sub");
    
    /*
    * This code builds a principal name from the iss and sub fields of the id_token. If
    * this user does not exist in the ISVA registry, either modify to map to a
    * local user that is in the registry, or change the EAI authentication
    * settings of the federation runtime to use PAC authentication. To use PAC
    * authentication, modify the following Federation -> Advanced Configuration:  
    *
    * poc.signIn.credResponseHeader = am-eai-pac 
    */
    stsuu.setPrincipalName(iss + "/" + sub);
    
    /*
    * Attributes from id_token come as attributes. 
    * Copy those attributes that you want to be built into the credential to the AttributeList. 
    * You can add to this list if you know what is in the id_token you expect. 
    * Only those attributes with values are copied.
    */
    var attrNames = [ 
        // these are standard claims
        "given_name", 
        "family_name",
        "name",
        "email",
        "access_token"
    ];
    var finalAttrs = [];
    
    for (var i = 0; i < attrNames.length; i++) {
        var attr = stsuu.getAttributeContainer().getAttributeByName(attrNames[i]);
        if (attr != null) {
            finalAttrs.push(attr);
        }
    }
    stsuu.clearAttributeList();
    
    /*
    * Add back in the final attributes
    */
    for (var i = 0; i < finalAttrs.length; i++) {
        stsuu.addAttribute(finalAttrs[i]);
    }
    
    let token =  stsuu.getContextAttributes().getAttributeValueByName("access_token");
    /*
    * Also pull these from context attributes (these are not available in the id_token)
    */
    var contextAttrNames = [
        "access_token",
        "expires_in", 
        "scope"
    ];
    
    for (var i = 0; i < contextAttrNames.length; i++) {
        var attr = stsuu.getContextAttributes().getAttributeByName(contextAttrNames[i]);
        if (attr != null) {
                stsuu.addAttribute(attr);
                stsuu.getContextAttributes().removeAttribute(attr);
        }
    }
    
    
    
    //IDMappingExtUtils.traceString("oidc_rp mapping rule finished with new stsuu: " + stsuu.toString());
    
    var headers = new Headers();
    
    headers.addHeader("Authorization", "Bearer "+token);
    var endpoint = null;
    var httpsTrustStore = "rt_profile_keys";
    var clientKeyStore = null;
    var clientKeyAlias = null;
    
    endpoint = "https://www.myidp.ibm.com/isam/sps/oauth/oauth20/userinfo";
    
    /* hr will be a com.ibm.security.access.httpclient.HttpResponse */
    IDMappingExtUtils.traceString("endpoint: " + endpoint + " httpsTrustStore: " + httpsTrustStore );
    
    var hr = HttpClientV2.httpGet(endpoint, headers, httpsTrustStore, null, null, clientKeyStore, clientKeyAlias);
    
    if(hr.getCode() == 200){
        IDMappingExtUtils.traceString(" body" + hr.getBody());
        var body = hr.getBody();
        var base_token = IDMappingExtUtils.stringToXMLElement('<wss:BinarySecurityToken	xmlns:wss="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" wss:EncodingType="http://ibm.com/2004/01/itfim/base64encode" 	wss:ValueType="urn:com:ibm:JWT">' +  body+ '</wss:BinarySecurityToken>');
    
        var res = LocalSTSClient.doRequest("http://schemas.xmlsoap.org/ws/2005/02/trust/Validate", "urn:appliesto", "urn:issuer", base_token, null)
        var assertionValid = (res.errorMessage == null);
        if (!assertionValid) {
        OAuthMappingExtUtils.throwSTSUserMessageException("Invalid Assertion. Authentication failed [" + res.errorMessage + "].");
        }
        var assertion_stsuu = new STSUniversalUser();
        assertion_stsuu.fromXML(res.token);
        IDMappingExtUtils.traceString(" jsonToken" + assertion_stsuu.toString());
        var jsonObj = JSON.parse(assertion_stsuu.getContextAttributes().getAttributeValueByName("claim_json"));
    
        IDMappingExtUtils.traceString(" json.sub" + jsonObj["sub"]);
    }
    
    
  4. Navigate to Federation -> Federations. Edit the OpenID Connect Relying Party federation.
    [block:image]
    {
    "images": [
    {
    "image": [
    "https://files.readme.io/ea58706-federation.png",
    "ea58706-federation.png",
    1648,
    700,
    "#000000",
    null,
    "660cc971cb3308006720941f"
    ]
    }
    ]
    }
    [/block]
    .
    * Navigate to Identity Mapping -> Use JavaScript transformation for identity mapping.

    1648
    * Select the identity mapping rule **OIDCRP**.
    
    [block:image]
    

    {
    "images": [
    {
    "image": [
    "https://files.readme.io/194d1dc-federation3.png",
    "194d1dc-federation3.png",
    1648,
    823,
    "#000000",
    null,
    "660cc973537f390010a2bc04"
    ]
    }
    ]
    }
    [/block]

    * Save and deploy pending changes.
    
  5. Navigate to Federation -> Federations. Select the federation and click Partners. [block:image]
    {
    "images": [
    {
    "image": [
    "https://files.readme.io/d0197da-partner.png",
    "d0197da-partner.png",
    1648,
    700,
    "#000000",
    null,
    "660cc9748c6701000f7e4d1a"
    ]
    }
    ]
    }
    [/block]

    * Select the partner and edit.
    * Navigate to Scope configuration and uncheck Perform userinfo request automatically [block:image]
    

    {
    "images": [
    {
    "image": [
    "https://files.readme.io/0ede2a3-partner1.png",
    "0ede2a3-partner1.png",
    1648,
    813,
    "#000000",
    null,
    "660cc975f0233f0042e304d6"
    ]
    }
    ]
    }
    [/block]
    .
    * Save and deploy pending changes.