Reputation: 12039
I am trying to make calls to the KeyCloak Admin API using the Java client. Regardless of which operation I try -- create a realm, create user accounts, etc. -- I get the following Exception:
javax.ws.rs.ProcessingException: java.lang.NullPointerException at org.keycloak.admin.client.resource.BearerAuthFilter.filter(BearerAuthFilter.java:53) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterRequest(ClientInvocation.java:573) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:438) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:102) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76) at com.sun.proxy.$Proxy214.create(Unknown Source)
My KeyCloak code looks like this...
Keycloak kc = KeycloakBuilder.builder().realm("master").clientId("admin-cli").username("admin") .password("password").serverUrl("http://localhost:8880/auth")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()).build();
RealmRepresentation realm = new RealmRepresentation();
realm.setDisplayName(displayName);
realm.setDisplayNameHtml(displayName);
realm.setRealm(realmName);
realm.setEnabled(enabled);
kc.realms().create(realm);
It is at this point I see the Exception. Any idea on what could be going wrong here? I see the Exception on ALL operations against the admin API. I know that the data is available, because I am able to make a curl call against the admin API and it works fine.
curl -vki -H "Authorization: Bearer XXXXX" http://localhost:8880/auth/admin/realms/master
When I make this call, I see the expected results. I only get failures when using the Java client. Any idea what's going on here??
UPDATE
I've increased the debugging logs on my KeyCloak instance. I see the following messages after attempting to create a security realm. (Note: I see similar errors regardless of what operation I perform)
14:36:16,553 DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-24) new JtaTransactionWrapper
14:36:16,553 DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-24) was existing? false
14:36:16,557 DEBUG [org.keycloak.authentication.AuthenticationProcessor] (default task-24) AUTHENTICATE CLIENT
14:36:16,557 DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (default task-24) client authenticator: client-secret
14:36:16,557 DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (default task-24) client authenticator SUCCESS: client-secret
14:36:16,557 DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (default task-24) Client admin-cli authenticated by client-secret
14:36:16,558 DEBUG [org.keycloak.authentication.AuthenticationProcessor] (default task-24) AUTHENTICATE ONLY
14:36:16,559 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) processFlow
14:36:16,559 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) check execution: direct-grant-validate-username requirement: REQUIRED
14:36:16,559 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) authenticator: direct-grant-validate-username
14:36:16,560 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) invoke authenticator.authenticate: direct-grant-validate-username
14:36:16,560 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) authenticator SUCCESS: direct-grant-validate-username
14:36:16,561 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) check execution: direct-grant-validate-password requirement: REQUIRED
14:36:16,561 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) authenticator: direct-grant-validate-password
14:36:16,561 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) invoke authenticator.authenticate: direct-grant-validate-password
14:36:16,658 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) authenticator SUCCESS: direct-grant-validate-password
14:36:16,658 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) check execution: direct-grant-validate-otp requirement: OPTIONAL
14:36:16,659 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) authenticator: direct-grant-validate-otp
14:36:16,659 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) invoke authenticator.authenticate: direct-grant-validate-otp
14:36:16,659 DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (default task-24) authenticator ATTEMPTED: direct-grant-validate-otp
14:36:16,661 DEBUG [org.keycloak.services.managers.AuthenticationSessionManager] (default task-24) Removing authSession 'df66e278-8ffe-47ab-84d1-36ffe9152021'. Expire restart cookie: true
14:36:16,673 DEBUG [org.keycloak.events] (default task-24) type=LOGIN, realmId=master, clientId=admin-cli, userId=c830af10-3cfd-4ce0-b1aa-83a857a290d7, ipAddress=172.18.0.1, auth_method=openid-connect, token_id=8876c7ad-3ea4-4356-8fc2-f9b50112b952, grant_type=password, refresh_token_type=Refresh, scope='profile email', refresh_token_id=4c2325fc-0bb5-40d9-af55-4985546fd39a, client_auth_method=client-secret, username=kcadmin
14:36:16,673 DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-24) JtaTransactionWrapper commit
14:36:16,676 DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-24) JtaTransactionWrapper end
UPDATE 2
It appears as though this issue is related to running this code within a Java EE container. If I run the exact same code in a standalone class, it works with no issue. However, if I copy and paste the code to a servlet and attempt to run it, I receive the NullPointerException
. I am running the code on Wildfly 13 when I encounter the problem. I also see this issue when running integration tests via Arquillian. My assumption is that it must have something to do with dependencies, but I'm at a loss as to what needs to be changed.
Upvotes: 7
Views: 10197
Reputation: 1600
Put simply (maybe even too much), there was a bug related to the Resteasy libraries, where the JsonBindingProvider
was given precedence over the ResteasyJackson2Provider
, which made some properties in json payloads not get mapped properly and their values get lost. Hence the NullPointerException.
The simplest solution if you're using JBoss or WildFly should be to exclude the resteasy-json-binding-provider
module from your deployment in the jboss-deployment-structure.xml
file:
<jboss-deployment-structure>
<deployment>
<exclusions>
<module name="org.jboss.resteasy.resteasy-json-binding-provider"/>
</exclusions>
</deployment>
</jboss-deployment-structure>
Upvotes: 5
Reputation: 193
If you want to keep Java EE 8 support of WildFly 13, just create empty jackson provider:
public class CustomJacksonProvider extends ResteasyJackson2Provider {
}
and add it to your KeyCloak builder using register()
method of ResteasyClientBuilder
:
Keycloak kc = KeycloakBuilder.builder()
.realm("master")
.clientId("admin-cli")
.username("admin")
.password("password")
.serverUrl("http://localhost:8880/auth")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).register(new CustomJacksonProvider()).build())
.build();
You also need to add some resteasy dependecies (as scope=provided
) in order to be able to use ResteasyClientBuilder
and ResteasyClientBuilder
:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.6.2.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>3.6.2.Final</version>
<scope>provided</scope>
</dependency>
Upvotes: 9
Reputation: 12039
Solved!! Apparently my Wildfly 13.0 instance was configured to enable Java EE 8 support. This seems to be the root cause of the issue, because removing that flag enabled all test cases to work as expected. Not sure why this was a problem, but it was definitely the underlying issue.
Upvotes: -3
Reputation:
You don't have an authorization header. Please try
Keycloak kc = KeycloakBuilder.builder().realm("master").clientId("admin-cli").username("admin") .password("password").authorization("authorization").serverUrl("http://localhost:8880/auth")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()).build();
Note the
.authorization("authorization")
Upvotes: 1