2013
25
February

Use Your Google Domain to Authenticate Liferay Users

This blog will cover some aspects of a login portlet implementation that will utilize the Google OpenID interface for user authentication. You might say that Liferay already provides that kind of implementation using its OpenID feature. However, implementing the authentication yourself gives you the opportunity to provide a custom login page and add some functionality to the login process that is not provided by the default Liferay implementation.

The implementation is based on the Liferay call OpenIdAction. We also have to take a closer look at the OpenIdAutoLogin implementation, which reads the OpenID user from the session and performs the login to Liferay.

Create a new Google Login Portlet

We start off with the creation of a new Portlet. Let’s call it GoogleLogin. I prefer to use the MVCPortlet implementation for this example.

liferay

liferay

After the portlet has been created, pull in the latest openid4java library and all it dependencies as Web Libraries. The latest version can be found at http://code.google.com/p/openid4java/downloads/list .

To start off, let’s change the view.jsp file that should have been created in the JSP folder of the portlet, in our case html/google_login.

<portlet:actionURL name="googleLogin" var="loginURL">
<aui:form action="<%= loginURL.toString() %>" method="POST">
    <aui:button-row>
        <aui:button type="submit" value="Login using Google">
	<!--<span class="hiddenSpellError" pre=""-->aui:button-row>
<!--<span class="hiddenSpellError" pre=""-->aui:form>

Implement the MVC Portlet

First, we have to initialize the openId4Java framework.

@Override
public void init(PortletConfig config) throws PortletException {
    super.init(config);
    consumerManager = new ConsumerManager();
    consumerManager.setAssociations(new InMemoryConsumerAssociationStore());
    consumerManager.setNonceVerifier(new InMemoryNonceVerifier(5000));
}

The GoogleLogin portlet has to implement two methods. The method that initiates the login process and a method that is called after the authentication has been completed. Since we are using the actionURL named ‘googleLogin’, we have to use that name as the first portlet method. The googleLogin method will check if the remote user has already been set. If not, an authentication request will be created and sent to Google.

private void sendAuthRequest(ActionRequest actionRequest, ActionResponse actionResponse, long companyId)
        throws IOException, SystemException, DiscoveryException, MessageException, ConsumerException
    {
        ...

        // get the openID using from the preferences, or default to google URL
        String openId = PrefsPropsUtil.getString(companyId, "openid.google.url", "https://www.google.com/accounts/o8/id");
        // create the returnTo URL for the authentication service
        LiferayPortletURL actionURL = PortletURLFactoryUtil.create(request, PortalUtil.getPortletId(actionRequest), themeDisplay.getPlid(), PortletRequest.ACTION_PHASE);
        actionURL.setParameter("p_p_state", "normal");
        actionURL.setParameter("p_p_mode", "view");
        // this is the method name of the MVCPortlet that will be called after the authentication request returns.
        actionURL.setParameter("javax.portlet.action","readOpenId");

        List discoveries = consumerManager.discover(openId);
        DiscoveryInformation discovered = consumerManager.associate(discoveries);
        session.setAttribute("openid-disco", discovered);
        AuthRequest authRequest = consumerManager.authenticate(discovered, actionURL.toString(),PortalUtil.getPortalURL(actionRequest));

        // in the addFetchRequest the attributes are added the the openID call should return
        addFetchRequest(authRequest);
        actionResponse.sendRedirect(authRequest.getDestinationUrl(true));
    }

The sendAuthRequest method builds the authentication URL for openID and sends it out.

private void sendAuthRequest(ActionRequest actionRequest, ActionResponse actionResponse, long companyId)
        throws IOException, SystemException, DiscoveryException, MessageException, ConsumerException
    {
        ...

        // get the openID using from the preferences, or default to google URL
        String openId = PrefsPropsUtil.getString(companyId, "openid.google.url", "https://www.google.com/accounts/o8/id");
        // create the returnTo URL for the authentication service
        LiferayPortletURL actionURL = PortletURLFactoryUtil.create(request, PortalUtil.getPortletId(actionRequest), themeDisplay.getPlid(), PortletRequest.ACTION_PHASE);
        actionURL.setParameter("p_p_state", "normal");
        actionURL.setParameter("p_p_mode", "view");
        // this is the method name of the MVCPortlet that will be called after the authentication request returns.
        actionURL.setParameter("javax.portlet.action","readOpenId");

        List discoveries = consumerManager.discover(openId);
        DiscoveryInformation discovered = consumerManager.associate(discoveries);
        session.setAttribute("openid-disco", discovered);
        AuthRequest authRequest = consumerManager.authenticate(discovered, actionURL.toString(),PortalUtil.getPortalURL(actionRequest));

        // in the addFetchRequest the attributes are added the openID call should return
        addFetchRequest(authRequest);
        actionResponse.sendRedirect(authRequest.getDestinationUrl(true));
    }

This completes the first part of the implementation – sending the request to Google. Now let’s proceed with reading the OpenID authentication response and taking the appropriate actions. The first part of the method verifies the response. The second part analyzes the returned user information.

public void readOpenId(ActionRequest actionRequest,ActionResponse actionResponse) throws Exception {
        ...
        // get the discovery information from the session
	DiscoveryInformation discovery = (DiscoveryInformation)session.getAttribute(OPEN_ID_DISCOVERY);

        // if discovery information are missing go back to the login page
        if (discovery == null) {
            sendRedirect(actionRequest, actionResponse);
            return;
        }

        String receivingUrl = ParamUtil.getString(actionRequest, "openid.return_to");
        // run the verification, if the verification failed go to the login page
        VerificationResult verification = consumerManager.verify( receivingUrl, params, discovery);

        Identifier verified = verification.getVerifiedId();

        if (verified == null) {
            sendRedirect(actionRequest, actionResponse);
            return;
        }

        // get the authentication information and login, create a new user if necessary
        AuthSuccess authSuccess = (AuthSuccess)verification.getAuthResponse();
        createUserIfRequired(request, authSuccess, themeDisplay);
}

In the second part of the response processing, we will try to find the user in Liferay. If he/she does not exist, we create a new user. The last step in the process is to store the user id in the session attribute “OPEN_ID_LOGIN”. The name of that attribute has to match the name that is used by the OpenIdAutoLogin implementation to locate the user and run the login script.

private String createUserIfRequired(HttpServletRequest request, AuthSuccess authSuccess, ThemeDisplay themeDisplay) throws Exception {
    HttpSession session = request.getSession(false);
    // we need to store the OpenID, lets take out any special characters.
    String openId = normalize(authSuccess.getIdentity());
    User user = null;

    try {
        // check if the user already exists in Liferay based in the openId
        user = UserLocalServiceUtil.getUserByOpenId(themeDisplay.getCompanyId(), openId);
    } catch (NoSuchUserException nue) {
        // read the user information from the response.
        ....
        user = createUser(firstName, lastName, emailAddress, openId, themeDisplay);
    }

    // the attribute name is important. It has to match the name that is used by OpenIdAutoLogin
    session.setAttribute("OPEN_ID_LOGIN", new Long(user.getUserId()));
    return null;
}

That completes the implementation of simple Google Open ID login portlet. You hook it into Liferay by adding the following line to the portal-ext.properties file:

auth.login.url=/web/guest/google-login

I will post the link to the complete portlet implementation as soon as it is available. For further references, please check https://developers.google.com/accounts/docs/OpenID and http://www.liferay.com.

Share with your peeps...Share on LinkedInShare on Google+Tweet about this on TwitterShare on FacebookEmail this to someone

Thomas Behlau combines powerful analytical skills with strategic decision making processes to provide client solutions as a Senior Consultant at Isos Technology.

As a seasoned software engineering consultant, Thomas Behlau is normally cast in the role of tech lead and architect in his consulting engagement. Thomas possesses a daunting list of skills as a software engineer. He has extensive experience in delivering enterprise and mobile applications. Thomas has been involved in and managed all phases of software development projects, from initial design through development, deployment and production support.

Beyond standard software engineering roles, Thomas Behlau has also produced and taught courses to enterprise clients. These have ranged from introductory OO courses to Java programming for RPG programmers to enterprise application server administration and best practices. Thomas brings this strong skill at mentoring to clients, leaving them with a solid understanding of the solutions provided.

Thomas Behlau applies the same thorough analysis and long term strategy to consulting problems as one would apply to an intense game of chess. His wide-ranging technical knowledge and strong mentoring skills make him a valuable asset to all Isos Technology's clients.

Tagged with: , , — Posted in Liferay