OpenId in a Web Application based on Java EE and JSF 2.0

This is an example how to connect your JSF 2.0 Website with an OpenId Provider by using the openid4java-library. It exists already some examples/tutorials for JSPs (see [1],[2] and [3]), but I found none based on JSF. So I decide to write my own. The followings are based on the examples above and the Seam OpenId implementation (see org.jboss.seams.security.openid).

First, add the openid4java library to the web applications class path. You find an installation description here.

Second step is to create two pages. One to fire up the login procedure and one to process the response. I called them index.xhtml (startpage) and openid.xhtml (redirectpage). The index.xhtml is quite simple. It looks like this:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:outputLabel for="openid">Open ID</h:outputLabel>
            <h:inputText id="openid_identifier" value="#{openid.userSuppliedId}"
                      style="background: url(http://openid.net/login-bg.gif) no-repeat; padding-left: 18px; background-position: 0 50%;" />
            <h:commandButton action="#{openid.login}" value="Login" id="openid"/>
        </h:form>
    </h:body>
</html>

To improve the user recognition, I styled the h:inputtext with the openid logo (see the style attribute “style=“).

The other page, the openid.xhtml presents the response from the openid provider and looks like:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:inputHidden value="#{openid.onLoad}"/>
 
            <h:outputLabel for="userSuppliedId" value="User Supplied Id: "/>
            <h:outputText id="userSuppliedId" value="#{openid.userSuppliedId}"/>
            <br/>
            <h:outputLabel for="fullname" value="Fullname: "/>
            <h:outputText id="fullname" value="#{openid.openIdFullName}"/>
            <br/>
            <h:outputLabel for="firstname" value="Firstname: "/>
            <h:outputText id="firstname" value="#{openid.openIdFirstName}"/>
            <br/>
            <h:outputLabel for="lastname" value="Lastname: "/>
            <h:outputText id="lastname" value="#{openid.openIdLastName}"/>
            <br/>
            <h:outputLabel for="country" value="Country: "/>
            <h:outputText id="country" value="#{openid.openIdCountry}"/>
            <!-- go on... --->
        </h:form>
    </h:body>
</html>

You may be notice the inputHidden field? Because of there is no page init method (like onLoad) in JSF, I create a hidden input-field, which shows (invisible) a dummy member. In the getter method of the member, I execute my init routine ;-). More about that later…

So let’s talk about the key element – my Session Bean. Some code fragments are copied from the seam openid implementation. Explanations are in and below the source code.

/*
 * Created tdmarti, HSLU T&amp;A - D3S (2011)
 */
package ch.hslu.d3s;
 
import java.io.IOException;
/** ... */
import org.openid4java.message.ax.FetchResponse;
 
/**
 * Authenticate the user against a openid provider
 * @author tdmarti
 */
@Named("openid")
@SessionScoped
public class OpenId implements java.io.Serializable {
 
    /** Creates a new instance of OpenId */
    public OpenId() {
    }
 
    private String userSuppliedId; //Users OpenID URL
    private String validatedId;
    private String openIdEmail;
    /* ... */
    private String onLoad;
    private ConsumerManager manager;
    private DiscoveryInformation discovered;
 
    public void login() throws IOException {
        try {
            manager = new ConsumerManager();
        } catch (ConsumerException e) {
            System.out.println("Error creating ConsumerManager.");
        }
        validatedId = null;
        String returnToUrl = returnToUrl("/openid.xhtml");
        String url = authRequest(returnToUrl);
 
        if (url != null) {
            FacesContext.getCurrentInstance().getExternalContext().redirect(url);
        }
    }
 
    /**
     * Create the current url and add another url path fragment on it.
     * Obtain from the current context the url and add another url path fragment at
     * the end
     * @param urlExtension f.e. /nextside.xhtml
     * @return the hole url including the new fragment
     */
    private String returnToUrl(String urlExtension) {
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        String returnToUrl = "http://" + request.getServerName() + ":" + request.getServerPort()
                + context.getApplication().getViewHandler().getActionURL(context, urlExtension);
        return returnToUrl;
    }
 
    /**
     * Create an authentication request.
     * It performs a discovery on the user-supplied identifier. Attempt it to 
     * associate with the OpenID provider and retrieve one service endpoint 
     * for authentication. It adds some attributes for exchange on the AuthRequest.
     * A List of all possible attributes can be found on @see http://www.axschema.org/types/
     * @param returnToUrl
     * @return the URL where the message should be sent
     * @throws IOException
     */
    private String authRequest(String returnToUrl) throws IOException {
        try {
            List discoveries = manager.discover(userSuppliedId);
            discovered = manager.associate(discoveries);
            AuthRequest authReq = manager.authenticate(discovered, returnToUrl);
 
            FetchRequest fetch = FetchRequest.createFetchRequest();
            fetch.addAttribute("email",
                "http://schema.openid.net/contact/email", true);
            /* Some other attributes ... */
 
            authReq.addExtension(fetch);
            return authReq.getDestinationUrl(true);
        } catch (OpenIDException e) {
            // TODO
        }
        return null;
    }
 
    public void verify() {
        ExternalContext context = javax.faces.context.FacesContext
                .getCurrentInstance().getExternalContext();
        HttpServletRequest request = (HttpServletRequest) context.getRequest();
        validatedId = verifyResponse(request);
    }
 
    /**
     * Set the class members with date from the authentication response.
     * Extract the parameters from the authentication response (which comes
     * in as a HTTP request from the OpenID provider). Verify the response,
     * examine the verification result and extract the verified identifier.
     * @param httpReq httpRequest
     * @return users identifier.
     */
    private String verifyResponse(HttpServletRequest httpReq) {
        try {
            ParameterList response =
                    new ParameterList(httpReq.getParameterMap());
 
            StringBuffer receivingURL = httpReq.getRequestURL();
            String queryString = httpReq.getQueryString();
            if (queryString != null &amp;&amp; queryString.length() &gt; 0) {
                receivingURL.append("?").append(httpReq.getQueryString());
            }
 
            VerificationResult verification = manager.verify(
                    receivingURL.toString(),
                    response, discovered);
 
            Identifier verified = verification.getVerifiedId();
            if (verified != null) {
                AuthSuccess authSuccess =
                        (AuthSuccess) verification.getAuthResponse();
 
                if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
                    FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX);
 
                    List emails = fetchResp.getAttributeValues("email");
                    openIdEmail = (String) emails.get(0);
                     /* Some other attributes ... */
                }
                return verified.getIdentifier();
            }
        } catch (OpenIDException e) {
            // TODO
        }
        return null;
    }
 
    /**
     * hidden member for onLoad/Init event. 
     *@return always return the string pageLoaded
     */
    public String getOnLoad() {
        verify();
        return "pageLoaded";
    }
 
    /**
     * Getter and Setter Method
     */
    public String getUserSuppliedId() {
        return userSuppliedId;
    }
    public void setUserSuppliedId(String userSuppliedId) {
        this.userSuppliedId = userSuppliedId;
    }
    public String getValidatedId() {
        return validatedId;
    }
    public String getOpenIdEmail() {
        return openIdEmail;
    }
/* Other getters ... */
}

After you deploy and start them you will see a page like this
JSF Page with OpenID Login
Now the user can identify itself by filling in his OpenId URL like http://<<username>>.myopenid.com or https://www.google.com/accounts/o8/id (google is also an OpenId provider, type the google URL and you will redirected to the google login page).

Explanation

I try to explain the most things in the javadoc – read it first.

Add new/other Attributes

FetchRequest fetch = FetchRequest.createFetchRequest();
fetch.addAttribute("email", "http://schema.openid.net/contact/email", true);

You can add others/more attributes on the request. You will find a list here. But be carefully not each OpenId Provider support the current valid specification. For example myopenid want the attribute http://schema.openid.net/contact/email or http://schema.openid.net/namePerson and not http://axschema.org/contact/email and http://axschema.org/namePerson.
My workaround is a if-then switch like (I omit it on the example above)

if (userSuppliedId.contains("myopenid")){
  fetch.addAttribute("email", "http://schema.openid.net/contact/email", true);
  fetch.addAttribute("fullname", "http://schema.openid.net/namePerson", true);
/* ... */
} else {
  fetch.addAttribute("email", "http://axschema.org/contact/email", true);
  fetch.addAttribute("fullname", "http://axschema.org/namePerson", true);
/* ... */

May be I post a bit later a list of different attributes, which I have found.

Fetch the answer

Be careful, the getAttributeValue parameter has to match to the first parameter on the addAttributemethod (case sentitiv).

List emails = fetchResp.getAttributeValues("email");
openIdEmail = (String) emails.get(0);
/* or also possible */
openIdEmail = fetchResp.getAttributeValue("email");

onLoad

As I already wrote, JSF has not a on Load or Init method, which is called very time a page is loaded. My workaround is to create a class member which is displayed in a hidden field on the page. Because of the sequentially execution, the hidden field should be placed on the top of the page and you get your onLoad event ;-).

public String getOnLoad() {
  verify(); //should call, when the page is loaded
  return "pageLoaded";
}
  1. Thanks very much!
    but have some errors, I need it to a final work for high school, the error say: “/index.xhtml @19,108 value=”#{openid.userSuppliedId}”: Target Unreachable, identifier ‘openid’ resolved to null” if you can send me the project to my e-mail, i would be very grateful thanks, and put your name in the thanks

  2. yes, i import the openid4java.jar, but don’t work, i am new in java
    thanks for your help =]

  3. CDI has to be activated in your project. (beans.xml file in the WEB-INF-Folder).

  4. thanks very much, but i am doing some thing wrong, when i can fix it i told to you ^^

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>