Javascript Custom Mapper - Return list instead of Object

We’re trying to make Keycloak work for a very peculiar openid implementation which requires a custom scope with a hardcoded name and a hardcoded claim using this format:

{
    //... other claims,
    "vikunja_groups": [
        {
            "name": "team 1",
            "oidcID": 33349
        },
        {
            "name": "team 2",
            "oidcID": 35933
        }
    ]
}

My basic idea is to create a script mapper as described here. A very simple first try (hardcoded claim instead of dynamically assigning it from the user client role assignment) is as follows:

// prints can be used to log information for debug purpose.
print("Starting Makerspace Vikunja Group Mapper");

var clientId = keycloakSession.getContext().getClient().getClientId();

var test = [{ name: clientId }];

exports = test;

I’d basically expect a claim like this:

"my_claim_name": [
    {
      "name": "my-client-name"
    }
],

What I’m getting instead is:

"my_claim_name": {
    "0": {
      "name": "my-client-name"
    }
  },

The mapper is created with Claim type set to JSON. Any idea if this is achievable?

Do not use the Javascript internal Maps and Arrays, use the Java types, they are better serialized. In the head of your script, import the Java types:

ArrayList = Java.type("java.util.ArrayList");
HashMap = Java.type("java.util.HashMap");

Then, in your script, use them as you would do it in native Java code:


var map1 = new HashMap();
map.put("key11", "value11");
map.put("key12", "value12");

var map2 = new HashMap();
map.put("key21", "value21");
map.put("key22", "value22");

var list = new ArrayList();
list.add(map1);
list.add(map2);

Then…

//add it to claim
token.setOtherClaims("my_claim_name", list);
//or return it
exports = list;

In the client setup, mapper return type JSON is ok.

Hi Matthias,

thanks so much for your help. Being able to work with the Java types actually makes it easier. As example I’m just adding all client roles assigned to the user for now:

// Import Java native types
ArrayList = Java.type("java.util.ArrayList");
HashMap = Java.type("java.util.HashMap");

// Get client we're operating on
var client = keycloakSession.getContext().getClient();

// Create group list
var list = new ArrayList();

// Iterate through all client role assignments and add value to vikunja_groups
user.getClientRoleMappingsStream(client).forEach(function (roleModel) {

    // Create a hash map for this role
    var role_map = new HashMap();
    role_map.put("oidcID", roleModel.getId());
    role_map.put("name", roleModel.getName());

    // Add it to the list
    list.add(role_map);

});

// Return the list
// token.setOtherClaims("vikunja_groups_test", list);
exports = list;

I like exporting more, as it’s then up to the user to define the claim name as part of the mapper config instead of hardcoding it in the pipeline. We just need to remember to set Multivalued to true, but like this it’s working as intended.

Thanks for your support!