Reputation: 33
I wanted to integrate keycloak as authentication plugin for nuxeo platform both running on my local machine
Set up details
Nuxeo platform version: 10.10 (runs on tomcat 9)
Keycloak version: 6.0.1
keycloak tomcat adapter distribution: keycloak-tomcat8-adapter-dist
I followed the steps mentioned in link https://github.com/nuxeo/nuxeo/tree/master/nuxeo-services/login/nuxeo-platform-login-keycloak.
Here, I built the nuxeo-platform-login-keycloak plugin for keycloak 6.0.1 version.
On keycloak, I set up a auth client under newly created realm 'demo'
Client details available in client configuration
I created role as 'Members' and added admin role to it I created a user 'keycloakuser' and added to 'Members'.
When nuxeo ui is hit from browser, the authentication flow works fine. It redirects me to login page of keycloak, on valid credentials, it redirects me to nuxeo ui. The user created along with 'Members' group assigned to it.
Error Scenario
To call rest api from postman, I configured Oauth2 for authentication.
Auth url: http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth
Token Url: http://localhost:8080/auth/realms/demo/protocol/openid-connect/token
Client: testclient
Client secret: *****
Scope: openid
I used access_token obtained using Oauth2 flow, to make API call as http://localhost:8190/nuxeo/api/v1/id/document_id. It is failing with
java.lang.ClassCastException: class org.apache.catalina.core.ApplicationHttpRequest cannot be cast to class org.apache.catalina.connector.RequestFacade (org.apache.catalina.core.ApplicationHttpRequest and org.apache.catalina.connector.RequestFacade are in unnamed module of loader java.net.URLClassLoader @39aeed2f)
at org.nuxeo.ecm.platform.ui.web.keycloak.DeploymentResult.invokeOn(DeploymentResult.java:79) [nuxeo-platform-login-keycloak-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.keycloak.KeycloakAuthenticatorProvider.provide(KeycloakAuthenticatorProvider.java:56) [nuxeo-platform-login-keycloak-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.keycloak.KeycloakAuthenticationPlugin.handleRetrieveIdentity(KeycloakAuthenticationPlugin.java:113) [nuxeo-platform-login-keycloak-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.handleRetrieveIdentity(NuxeoAuthenticationFilter.java:1137) [nuxeo-platform-web-common-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.doFilterInternal(NuxeoAuthenticationFilter.java:548) [nuxeo-platform-web-common-10.10.jar:?]
Observation:
1. The API request call is not hitting the keycloak endpoint
2. I tried to print the reqqest type (actually the request wrapper type) in both scenarios.
For browser request, it was org.apache.catalina.connector.RequestFacade
and for api request it was org.apache.catalina.core.ApplicationHttpRequest
which is not extending org.apache.catalina.connector.RequestFacade
Questions:
1. Does above behavior (mentioend in point 2) differ in tomcat versions earlier to tomcat 9?
2. Is the problem with compatibility issues with tomcat version and keycloak adapters jar version?
Upvotes: 1
Views: 782
Reputation: 11
Late answer, but it may come handy for new readers. I had the exact same issue some months ago. It seems to be due to a bug in the nuxeo-platform-login-keycloak plugin.
I endend making the following changes to org.nuxeo.ecm.platform.ui.web.keycloak.DeploymentResult
:
public class DeploymentResult {
final static Logger LOGGER = LoggerFactory.getLogger(DeploymentResult.class);
private boolean isOk;
private static KeycloakDeployment keycloakDeployment;
private HttpServletRequest httpServletRequest;
private HttpServletResponse httpServletResponse;
private Request request;
private CatalinaHttpFacade facade;
public DeploymentResult(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
this.httpServletRequest = httpServletRequest;
this.httpServletResponse = httpServletResponse;
}
boolean isOk() {
return isOk;
}
public static KeycloakDeployment getKeycloakDeployment() {
return keycloakDeployment;
}
public Request getRequest() {
return request;
}
public CatalinaHttpFacade getFacade() {
return facade;
}
public DeploymentResult invokeOn(AdapterDeploymentContext deploymentContext) {
// In Tomcat, a HttpServletRequest and a HttpServletResponse are wrapped in a Facades
if (httpServletRequest instanceof RequestFacade) {
// Received upon logout.
request = unwrapRequest(httpServletRequest);
} else {
request = unwrapRequest(((ServletRequestWrapper) httpServletRequest).getRequest());
}
facade = new CatalinaHttpFacade(httpServletResponse, request);
if (keycloakDeployment == null) {
keycloakDeployment = deploymentContext.resolveDeployment(facade);
}
if (keycloakDeployment.isConfigured()) {
isOk = true;
return this;
}
isOk = false;
return this;
}
/**
* Get the wrapper {@link Request} hidden in a {@link ServletRequest} object
*
* @param servletRequest, the main ServletRequest object
* @return the wrapper {@link Request} in {@link ServletRequest}
*/
private Request unwrapRequest(final ServletRequest servletRequest) {
try {
final Field f = servletRequest.getClass().getDeclaredField("request");
f.setAccessible(true); // grant access to (protected) field
return (Request) f.get(servletRequest);
} catch (final NoSuchFieldException | IllegalAccessException e) {
LOGGER.error("Couldn't unwrap request", e);
throw new RuntimeException(e);
} catch (final Exception e) {
LOGGER.error("Couldn't unwrap request", e);
throw e;
}
}
}
After building and deploying the plugin with these changes, I was allowed to call Nuxeo's REST API with bearer authentication using access token obtained through Keycloak.
Disclaimer: I focused on making it work, not on making it clean...
Upvotes: 1