Thomas Lann
Thomas Lann

Reputation: 1234

Keycloak Protocol Mapper that adds claim to JWT gets Null Pointer Exception

I'm having a rough time getting a very simple Keycloak ProtocolMapper working in Keycloak 6.0.1.
I'm trying to add extra claims to an access token. When I copy the built jar to the deployment directory I get the following NullPointerException in the deployment directory with the below contents in .failed file.

{"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"token-enhancer-1.0-SNAPSHOT.jar\".POST_MODULE" => "WFLYSRV0153: Failed to process phase POST_MODULE of deployment \"token-enhancer-1.0-SNAPSHOT.jar\"
    Caused by: java.lang.NullPointerException"}}

The logs show the same exception right below

21:18:46,697 INFO  [org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor] (MSC service thread 1-1) Deploying Keycloak provider: token-enhancer-1.0-SNAPSHOT.jar
21:18:46,718 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC000001: Failed to start service jboss.deployment.unit."token-enhancer-1.0-SNAPSHOT.jar".POST_MODULE: org.jboss.msc.service.StartException in service jboss.deployment.unit."token-enhancer-1.0-SNAPSHOT.jar".POST_MODULE: WFLYSRV0153: Failed to process phase POST_MODULE of deployment "token-enhancer-1.0-SNAPSHOT.jar"
        at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:183)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1738)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1700)
        at org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1558)
        at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
        at org.keycloak.services.DefaultKeycloakSessionFactory.isInternal(DefaultKeycloakSessionFactory.java:370)
        at org.keycloak.services.DefaultKeycloakSessionFactory.loadFactories(DefaultKeycloakSessionFactory.java:219)
        at org.keycloak.services.DefaultKeycloakSessionFactory.deploy(DefaultKeycloakSessionFactory.java:115)
        at org.keycloak.provider.ProviderManagerRegistry.deploy(ProviderManagerRegistry.java:42)
        at org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor.deploy(KeycloakProviderDeploymentProcessor.java:55)
        at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:176)
        ... 8 more

21:18:46,719 ERROR [org.jboss.as.controller.management-operation] (DeploymentScanner-threads - 1) WFLYCTL0013: Operation ("full-replace-deployment") failed - address: ([]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"token-enhancer-1.0-SNAPSHOT.jar\".POST_MODULE" => "WFLYSRV0153: Failed to process phase POST_MODULE of deployment \"token-enhancer-1.0-SNAPSHOT.jar\"
    Caused by: java.lang.NullPointerException"}}

I've upload sample code to github.

My implemented ProtocolMapper class is below.

I'm having a rough time because I don't understand where to troubleshoot with such a vague error message. What would be a good strategy to investigate this issue?

import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.*;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;

import java.util.*;

public class KeycloakTokenEnhancer extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {

    public static final String PROVIDER_ID = "oidc-token-enhancer-mapper";

    private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();

    static {
        OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, KeycloakTokenEnhancer.class);
    }

    @Override
    public AccessToken transformAccessToken(AccessToken accessToken, ProtocolMapperModel protocolMapperModel, KeycloakSession keycloakSession, UserSessionModel userSessionModel, ClientSessionContext clientSessionContext) {
        return accessToken;
    }

    @Override
    public String getDisplayCategory() {
        return "Token Enhancer mapper";
    }

    @Override
    public String getDisplayType() {
        return "Token Enhancer Mapper";
    }

    @Override
    public String getHelpText() {
        return "Add to claims for the User Service";
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        return configProperties;
    }

    @Override
    public String getId() {
        return PROVIDER_ID;
    }


    protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
        token.getOtherClaims().put("fruit", "pear, apple, tangerine");
    }

    public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo) {
        ProtocolMapperModel mapper = new ProtocolMapperModel();
        mapper.setName(name);
        mapper.setProtocolMapper(PROVIDER_ID);
        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
        Map<String, String> config = new HashMap<String, String>();
        if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
        if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
        if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
        mapper.setConfig(config);
        return mapper;
    }


}

Upvotes: 0

Views: 1588

Answers (1)

Kohei TAMURA
Kohei TAMURA

Reputation: 5122

Try adding a package statement at the top of your class (KeycloakTokenEnhancer) and moving your class into the package.

NPE is thrown from line 370 in DefaultKeycloakSessionFactory:

String packageName = factory.getClass().getPackage().getName();

See also: Javadoc of Class.html#getPackage

Returns:
the package of the class, or null if no package information is available from the archive or codebase.

Upvotes: 1

Related Questions