RE: Liferay 7 authorization using openId connect and Keycloak

thumbnail
William Gosse, modified 6 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts

I'm using the OpenId Connect plugin, https://web.liferay.com/marketplace/-/mp/application/78695724, in combination with Keycloak to perform SSO authentication. This part is working out great.  

I'm trying to write a post login hook that will get me the roles that I have setup in Keycloak for an individual user that was authenticated by the plugin.  I thought I could use the Keycloak API to get this information but now I'm not so sure about that.  I tried the following:

 

HttpServletRequest request = lifecycleEvent.getRequest();

KeycloakPrincipal principal = (KeycloakPrincipal) request.getUserPrincipal();

if (principal != null) {

   String clientId = "aimportal";

   Set<String> roles = principal.getKeycloakSecurityContext().getToken().getResourceAccess(clientId).getRoles();

   for (String role : roles) {

       System.out.println("role=" + role);

   }

}

 

But a get a  java.lang.ClassCastException: com.liferay.portal.kernel.servlet.ProtectedPrincipal cannot be cast to org.keycloak.KeycloakPrincipal

 

 

I'd be happy if I could just get the JWT access token from the I'm assuming the request somehow.

 

I'm sure Im incorrectly going about this so any suggestion would be greatly appreciated.

 

thumbnail
William Gosse, modified 6 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts
Nobody know anything about this???
thumbnail
William Gosse, modified 6 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts
Well I was able to work around this issue by using keycloaks rest api to optain each role I was concerned with membership.  I was hoping to avoid that but...
thumbnail
William Gosse, modified 6 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts

By the way I did this keycloak/liferay integration using a post login hook and custom porlet in  combination with the openid connect plugin form the marketplace.  The openid connect plugin would authenticate the user using a jwt access token that was provided by keycloak. It also would create the user account within liferay if it did'nt exist. Just aheads up, in keycloak you need to have users that have a username, firstname, lastname and email address in order for them to be added into liferay by the plugin. 

 

Also roles in keycloak should asscociated directly with the users in keycloak. The keycloak rest api, although extensive, is somewhat quirky. The expedient way that I could find to get users roles was to make the following restful call:

GET /{realm}/roles/{role-name}/users

 

This call needs to be proceeded by a restful call to get the admin users access token.  I used a Jersey client inside my custom authorization portlet to make thes to calls. Inside this same portlet a also used the liferay apis to add the current user to the appropriate roles if needed within liferay. I would also remove a role from a user in liferay if they no longer had it in keycloak. 

 

The authorization portlet would then redirect the users to the appropriate landing page in liferay or an error page if they had no valid roles other then user.

 

The post login hook i created simple redirects each authenticated user to the  authorization portlet.

 

This may not be the best approach but it works.

thumbnail
William Gosse, modified 6 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts
I summarized the work I did for this in a blog I recently posted: Liferay 7 SSO using OpenId Connect
thumbnail
William Gosse, modified 6 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts
I should mention to access the user's roles in cloak there is one thing that must be done in order for the code I have above to work correctly.  When entering the username and email address for the user in Keycloak the username must match the first portion of the address before the @ sign.  This is because Liferay will use that first portion to create the  user's screen name in Liferay.  My code uses the screen name to call back to Keycloak in order to retrieve the roles using Keycloak's Rest API. I know its not a perfect solution and I may need to rethink this at a later date.
thumbnail
William Gosse, modified 5 Years ago. Liferay Master Posts: 533 Join Date: 7/4/10 Recent Posts
One thing I recently became aware of is that many of the Keycloak Restful API calls have a max query parameter to limit the number a of records that the call actually returns.  What the Keycloak documentation neglected to mention is that there is a default for this max parameter which seems to be normally set to 100 records.  I ran into this default setting on the one call I was making in my code above:
GET /{realm}/clients/{id}/roles/{role-name}/users
I had to add the setting of the max query parameter to overcome this limit of 100: 
Map<string, string> params = new HashMap<string, string>();
params.put(MAX, AUTH_SERVICE_ROLES_MAX);
String roleUserPath = AUTH_SERVICE_ROLES_PATH + SLASH + authServiceRole + USERS_PATH;
Response response = client.get(AUTH_SERVICE_URL, roleUserPath, MediaType.APPLICATION_JSON, params, accessToken);</string,></string,>
It would be better if the API didn't limit the call with a default unless specified by the developer.