<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <title>Liferay 7.4-ga112, Keycloak &amp; OpenID Connect : Login not working</title>
  <link rel="self" href="https://liferay.dev/c/message_boards/find_thread?p_l_id=119785294&amp;threadId=122700306" />
  <subtitle>Liferay 7.4-ga112, Keycloak &amp; OpenID Connect : Login not working</subtitle>
  <id>https://liferay.dev/c/message_boards/find_thread?p_l_id=119785294&amp;threadId=122700306</id>
  <updated>2026-04-04T05:30:31Z</updated>
  <dc:date>2026-04-04T05:30:31Z</dc:date>
  <entry>
    <title>RE: RE: Liferay 7.4-ga112, Keycloak &amp; OpenID Connect : Login not working</title>
    <link rel="alternate" href="https://liferay.dev/c/message_boards/find_message?p_l_id=119785294&amp;messageId=122702845" />
    <author>
      <name>Akash Jaiswal</name>
    </author>
    <id>https://liferay.dev/c/message_boards/find_message?p_l_id=119785294&amp;messageId=122702845</id>
    <updated>2024-06-11T15:41:32Z</updated>
    <published>2024-06-05T14:33:47Z</published>
    <summary type="html">&lt;p&gt;Thank you for your answer Rab,&lt;/p&gt;
&lt;p&gt;The exception is coming in the default Liferay class, not in the
  class I have overwritten. Our custom class is not getting executed (overriding).&lt;/p&gt;
&lt;p&gt;In 7.2 this is how we were using to inforce our custom class to
  override the liferay's class.&lt;/p&gt;
&lt;p&gt;

  &lt;strong&gt;com.liferay.portal.security.sso.openid.connect.internal.OpenIdConnectServiceHandlerImpl.config&lt;/strong&gt;&amp;nbsp;file under&amp;nbsp;&lt;strong&gt;osgi/configs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;
  &lt;u&gt;
    &lt;em&gt;
      &lt;strong&gt;_oIDCUserInfoProcessor.target = "(component.name=com.abc.xyz.portal.security.sso.oidc.PortalOpenIdConnectUserInfoProcessor)"&lt;/strong&gt;
    &lt;/em&gt;
  &lt;/u&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;But this is not the case in the 7.4.&lt;/p&gt;
&lt;p&gt;We do not think, this should the the correct file name
  &lt;strong&gt;"com.liferay.portal.security.sso.openid.connect.internal.OpenIdConnectServiceHandlerImpl.config&amp;nbsp;&lt;/strong&gt;"
  under osgi/configs to resolve this issue.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</summary>
    <dc:creator>Akash Jaiswal</dc:creator>
    <dc:date>2024-06-05T14:33:47Z</dc:date>
  </entry>
  <entry>
    <title>RE: Liferay 7.4-ga112, Keycloak &amp; OpenID Connect : Login not working</title>
    <link rel="alternate" href="https://liferay.dev/c/message_boards/find_message?p_l_id=119785294&amp;messageId=122702926" />
    <author>
      <name>Zsigmond Rab</name>
    </author>
    <id>https://liferay.dev/c/message_boards/find_message?p_l_id=119785294&amp;messageId=122702926</id>
    <updated>2024-06-05T13:54:47Z</updated>
    <published>2024-06-05T13:54:47Z</published>
    <summary type="html">&lt;p&gt;Hi Akash,&lt;/p&gt;
&lt;p&gt;Taking a quick look at the exception, it seems still the original
  method is called which throws an NPE from the&amp;nbsp;&lt;a href="https://github.com/liferay/liferay-portal/blob/7.4.3.120-ga120/modules/apps/portal-security-sso/portal-security-sso-openid-connect-impl/src/main/java/com/liferay/portal/security/sso/openid/connect/internal/OIDCUserInfoProcessor.java#L514"&gt;https://github.com/liferay/liferay-portal/blob/7.4.3.120-ga120/modules/apps/portal-security-sso/portal-security-sso-openid-connect-impl/src/main/java/com/liferay/portal/security/sso/openid/connect/internal/OIDCUserInfoProcessor.java#L514&lt;/a&gt; line.&lt;/p&gt;
&lt;p&gt;This could be investigated to find the reason.&lt;/p&gt;
&lt;p&gt;I's also suspicious that the parameters are different for your
  &lt;em&gt;processUserInfo&lt;/em&gt; method.&lt;/p&gt;
&lt;p&gt;Here you can find a public custom OIDC user resolver:&amp;nbsp;&lt;a href="https://github.com/fabian-bouche-liferay/oidc-user-resolver/tree/master"&gt;https://github.com/fabian-bouche-liferay/oidc-user-resolver/tree/master&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Regards,&lt;br&gt; Zsigmond&lt;/p&gt;</summary>
    <dc:creator>Zsigmond Rab</dc:creator>
    <dc:date>2024-06-05T13:54:47Z</dc:date>
  </entry>
  <entry>
    <title>Liferay 7.4-ga112, Keycloak &amp; OpenID Connect : Login not working</title>
    <link rel="alternate" href="https://liferay.dev/c/message_boards/find_message?p_l_id=119785294&amp;messageId=122700305" />
    <author>
      <name>Akash Jaiswal</name>
    </author>
    <id>https://liferay.dev/c/message_boards/find_message?p_l_id=119785294&amp;messageId=122700305</id>
    <updated>2024-06-05T08:59:37Z</updated>
    <published>2024-06-04T13:46:41Z</published>
    <summary type="html">&lt;p&gt;Hello Everyone, I am trying to Connect Liferay with Keycloak, But
  after login, I get the below screen.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;And the error:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;2024-06-04 11:36:37.734 ERROR
    [http-nio-9015-exec-5][StatusDisplayContext:83] Error: null&lt;br&gt;
    2024-06-04 11:37:52.841 WARN
    &amp;nbsp;[http-nio-9015-exec-8][PortalImpl:6128] null&lt;br&gt;
    java.lang.NullPointerException: null&lt;br&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;at
    com.liferay.portal.security.sso.openid.connect.internal.OIDCUserInfoProcessor._getUserId(OIDCUserInfoProcessor.java:514)
    ~[bundleFile:?]&lt;br&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;at
    com.liferay.portal.security.sso.openid.connect.internal.OIDCUserInfoProcessor.processUserInfo(OIDCUserInfoProcessor.java:60)
    ~[bundleFile:?]&lt;br&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;at
    com.liferay.portal.security.sso.openid.connect.internal.OpenIdConnectAuthenticationHandlerImpl.processAuthenticationResponse(OpenIdConnectAuthenticationHandlerImpl.java:143)
    ~[bundleFile:?]&lt;br&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;at
    com.liferay.portal.security.sso.openid.connect.internal.servlet.filter.auto.login.OpenIdConnectAutoLoginFilter.processFilter(OpenIdConnectAutoLoginFilter.java:87)
    [bundleFile:?]&lt;br&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;at
    com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:40) [portal-kernel.jar:?]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is my OpenIdUserInfoConnectorClass:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;package com.abc.xyz.portal.security.sso.oidc;

import com.abc.xyz.portal.security.sso.oidc.util.UserGroupUtil;
import com.abc.xyz.portal.security.sso.oidc.util.UserUtil;
import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.UserEmailAddressException;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONFactoryUtil;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Company;
import com.liferay.portal.kernel.model.Group;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.CompanyLocalService;
import com.liferay.portal.kernel.service.ContactLocalService;
import com.liferay.portal.kernel.service.GroupLocalService;
import com.liferay.portal.kernel.service.UserGroupLocalService;
import com.liferay.portal.kernel.service.UserLocalService;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.security.sso.openid.connect.OpenIdConnectServiceException;
import com.liferay.portal.security.sso.openid.connect.OpenIdConnectServiceException.UserMappingException;
import com.liferay.portal.security.sso.openid.connect.internal.OIDCUserInfoProcessor;
import com.liferay.portal.security.sso.openid.connect.internal.exception.StrangersNotAllowedException;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;


@Component(
        immediate = true,
        property = {"service.ranking:Integer=100"},
        service = OIDCUserInfoProcessor.class
)
public class PortalOpenIdConnectUserInfoProcessor extends OIDCUserInfoProcessor {

    private static final String USER_INFORMATION = "userInformation";
    private static final String SCREEN_NAME = "username";
    private static final String EMAIL_ADDRESS = "email";
    private static final String FIRST_NAME = "firstName";
    private static final String LAST_NAME = "lastName";
    private static final String PERSON_ID = "personId";
    private static final String ROLES = "roles";
    private static final String INDUSTRY = "industry";


    public long processUserInfo(UserInfo userInfo, long companyId)
            throws PortalException {

        _log.debug("userInfo: " + userInfo.toJSONObject().toJSONString());

        //Keycloak returns response in JSON format, so creating JSON object from response. Later extracting industry name from the JSON Object
        JSONObject userInfoJSON = JSONFactoryUtil.createJSONObject(userInfo.toJSONObject().toJSONString());
        JSONObject customUserInfo = userInfoJSON.getJSONObject(USER_INFORMATION);

        checkUserInfoValidity(customUserInfo);

        String screenName = customUserInfo.getString(SCREEN_NAME);
        String emailAddress = customUserInfo.getString(EMAIL_ADDRESS);
        String firstName = customUserInfo.getString(FIRST_NAME);
        String lastName = customUserInfo.getString(LAST_NAME);
        String personId = customUserInfo.getString(PERSON_ID);
        _log.info("ScreenName ooutput -1 =====================================================&amp;gt;&amp;gt;"+screenName);
        screenName = getUsernamefromEmailAddress(screenName);
        _log.info("ScreenName ooutput=====================================================&amp;gt;&amp;gt;"+screenName);
        checkUserProfileValidity(screenName, emailAddress, firstName, lastName, personId);


        //ADDED FOR MULTI-TENANCY. IDENTIFYING THE SITE(GROUP) FROM INDUSTRY NAME SO THAT THE USER CAN BE ADDED TO MEMBERSHIP OF THE SITE
        String industry = userInfoJSON.getString(INDUSTRY);
        Group site = null;
        long groupId = 0;
        try
        {
            site = _groupLocalService.getFriendlyURLGroup(companyId, StringPool.SLASH + industry); //find site(group) that has friendly url /industry_name
            groupId = site.getGroupId();
        }
        catch (PortalException e)
        {
            _log.error("Error occured while trying to find site from tenant:"+industry, e);
        }


        JSONArray roles = customUserInfo.getJSONArray(ROLES);
        String[] roleNames = getRoleNames(roles);
        long[] userGroupIds = UserGroupUtil.getUserGroupIds(companyId, roleNames, _userGroupLocalService);

        User user = null;
        String industryUserScreenName = screenName+StringPool.AT+industry;

        user = _userLocalService.fetchUserByScreenName(companyId, industryUserScreenName);
        if (user != null) {
            UserUtil.updateUser(user, companyId, industryUserScreenName, emailAddress, firstName, lastName, personId, userGroupIds, _companyLocalService, _userLocalService, _userGroupLocalService,  _contactLocalService, groupId);
            return user.getUserId();
        }

        checkAddUser(companyId, emailAddress);

        user = UserUtil.addUser(companyId, industryUserScreenName, emailAddress, firstName, lastName, personId, userGroupIds, _companyLocalService, _userLocalService, _contactLocalService, groupId);

        return user.getUserId();
    }


    private String[] getRoleNames(JSONArray roles) {
        String[] roleNames = new String[0];

        if (Validator.isNotNull(roles)) {
            roleNames = new String[roles.length()];
            for (int i = 0; i &amp;lt; roles.length(); i++) {
                roleNames[i] = roles.getString(i);
            }
        }

        return roleNames;
    }


    private void checkUserInfoValidity(JSONObject customUserInfo) throws UserMappingException  {
        if (customUserInfo == null) {
            throw new OpenIdConnectServiceException.UserMappingException("Invalid OIDC user info: userInformation claim is missing!");
        }

        String message = customUserInfo.getString("message");
        String status = customUserInfo.getString("status");
        String data = customUserInfo.getString("data");

        if (Validator.isNotNull(message)) {
            StringBundler sb = new StringBundler(9);

            sb.append("Unable to map OpenId Connect user to the portal, ");
            sb.append("Received error data: ");
            sb.append("{status=");
            sb.append(status);
            sb.append(", message=");
            sb.append(message);
            sb.append(", data=");
            sb.append(data);
            sb.append("}");

            throw new OpenIdConnectServiceException.UserMappingException(
                    sb.toString());
        }
    }

    private void checkUserProfileValidity(String screenName, String emailAddress, String firstName, String lastName,
                                          String personId) throws UserMappingException {

        if (Validator.isNull(screenName) || Validator.isNull(firstName) || Validator.isNull(lastName)
                || Validator.isNull(personId)) {

            StringBundler sb = new StringBundler(9);

            sb.append("Unable to map OpenId Connect user to the portal, ");
            sb.append("missing or invalid profile information: ");
            sb.append("{screenName=");
            sb.append(screenName);
            sb.append(", emailAddress=");
            sb.append(emailAddress);
            sb.append(", firstName=");
            sb.append(firstName);
            sb.append(", lastName=");
            sb.append(lastName);
            sb.append(", personId=");
            sb.append(personId);
            sb.append("}");

            throw new OpenIdConnectServiceException.UserMappingException(
                    sb.toString());
        }
    }

    public static String getUsernamefromEmailAddress(String userName){
        try{
            if(userName.contains("@")){
                String email = userName;
                int index = email.indexOf('@');
                email = email.substring(0,index);
                return email;
            }else{
                return userName;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return userName;

    }

    protected void checkAddUser(long companyId, String emailAddress)
            throws PortalException {

        Company company = _companyLocalService.getCompany(companyId);

        if (!company.isStrangers()) {
            throw new StrangersNotAllowedException(companyId);
        }

        if (!company.isStrangersWithMx() &amp;amp;&amp;amp;
                company.hasCompanyMx(emailAddress)) {

            throw new UserEmailAddressException.MustNotUseCompanyMx(
                    emailAddress);
        }
    }

    @Reference
    private CompanyLocalService _companyLocalService;

    @Reference
    private UserLocalService _userLocalService;

    @Reference
    private UserGroupLocalService _userGroupLocalService;

    @Reference
    private ContactLocalService _contactLocalService;

    @Reference
    private GroupLocalService _groupLocalService;

    private static Log _log = LogFactoryUtil.getLog(PortalOpenIdConnectUserInfoProcessor.class);

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To execute this, I have added below property
  in&amp;nbsp;&lt;strong&gt;com.liferay.portal.security.sso.openid.connect.internal.OpenIdConnectServiceHandlerImpl.config&lt;/strong&gt;
  file under &lt;strong&gt;osgi/configs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;
  &lt;u&gt;
    &lt;em&gt;
      &lt;strong&gt;_oIDCUserInfoProcessor.target = "(component.name=com.abc.xyz.portal.security.sso.oidc.PortalOpenIdConnectUserInfoProcessor)"&lt;/strong&gt;
    &lt;/em&gt;
  &lt;/u&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I am trying to identify the issue, but couldn't find anything. Can
  someone help me on this.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Best Regards&lt;/p&gt;
&lt;p&gt;Akash&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</summary>
    <dc:creator>Akash Jaiswal</dc:creator>
    <dc:date>2024-06-04T13:46:41Z</dc:date>
  </entry>
</feed>
