I ended up making my own custom Keycloak ProtocolMapper that extends from the default UserClientRoleMappingMapper. The trick was to get 'clientId' by means of the ClientSessionContext in the setClaim-method. The default UserClientRoleMappingMapper sort of allows for the same functionality, but you have to manually specify the clientId in the mapper instance. Since this is now done automatically, I remove this option in 'CONFIG_PROPERTIES'.
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.UserClientRoleMappingMapper;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.utils.RoleResolveUtil;
import java.util.ArrayList;
import java.util.List;
public class UserClientRoleMapper extends UserClientRoleMappingMapper {
public static final String MAPPER_PROVIDER_ID = "oidc-client-role-mapper-scoped";
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<>();
@Override
public List<ProviderConfigProperty> getConfigProperties() {
if (CONFIG_PROPERTIES.size() > 0) {
return CONFIG_PROPERTIES;
}
CONFIG_PROPERTIES.addAll(super.getConfigProperties());
CONFIG_PROPERTIES.removeIf(property -> property.getName().equals(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID));
return CONFIG_PROPERTIES;
}
@Override
public String getId() {
return MAPPER_PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "User Client Role Scoped";
}
@Override
public String getHelpText() {
return "Map a user client role to a token claim.";
}
@Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session, ClientSessionContext clientSessionCtx) {
String clientId = clientSessionCtx.getClientSession().getClient().getClientId();
String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX);
if (clientId != null && !clientId.isEmpty()) {
AccessToken.Access access = RoleResolveUtil.getResolvedClientRoles(session, clientSessionCtx, clientId, false);
if (access == null) {
return;
}
UserClientRoleMappingMapper.setClaim(token, mappingModel, access.getRoles(), clientId, rolePrefix);
}
}
}