keycloakjboss-tools

Compute Keycloak client attribute using scriptMapper


I'm using Keycloak as an Open ID connect provider, I want to connect my keycloak instance to a service through OpenId. But this services requires user info to provide a numeric identifier (which keycloak does not provides). I thought I could use the script mapper for that task and takes key cloak internal id (an uuid) and generate a numeric id from it.

The script mapper is supposed to accept javascript but there is no documentation nor playground to help me debug results.

This piece of code seems to work but I can't manage to get the result in my user info.

var id = user.Id.replace(/-/g, '').substring(0,10);
var fake_id = parseInt(id, 16);
fake_id

This code is accepted but i cannot debug it. when I try return fake_id; I get the error : Invalid return statement return fake_id;

Thanks for your help !


Solution

  • Your code is almost good. I have check it on Keycloak 6.0.1. There are, however, a few things that you should check and take into consideration:

    1. JS script shouldn't explicitly have any return statement. Its last statement is taken as a return value. On Keylcoak 6.0.1 adding explicit return leads to validation error.

    2. Ensure that you have "Add to userinfo" enabled as shown in picture below:

    enter image description here

    Note: For the sake of tests, you can also enable 'Add to access token'. In that case you can just request access token using resource owner password credentials grant and check if mapper works as intended.

    1. There is a couple of things that needs to be changed in your JS code:
      • when parsing int based on 10 characters, the resulting number is to large to be parsed as int. I got an int when I've lover this to 6 characters
      • use Math.floor() on resulting number to be sure that it will be an int. It is strange but I found out that even simple parseInt("10") results in Double not an Integer. I believe that this is Nashorn's (Javascript Engine that Keycloak uses) peculiarity

    var id = user.id.replace(/-/g, '').substring(0,6);
    var fake_id = parseInt(id, 16);
    Math.floor(fake_id);

    After adding a mapper, I got id such as 14401067 when requesting on auth/realms/example/protocol/openid-connect/token endpoint (I do requests using Insomnia, it has nice support for Auth so you can request directly /userinfo endpoint if you want - Insomnia will take care of requesting an access token first).

    Writing such mapper gets you an integer based on UUID. However, after giving it a thought, this is not an ideal approach - it doesn't really guarantee you an uniqueness. This is because we shorten UUID that is 128 bit value to an int. As a consequence, there can be collisions. If your system strongly relies on uniqueness of this id, then you should rethink your approach. For sure, it is possible to explicitly set an attribute of an user but this requires manual management so it may not be an option.