January 31, 2012

Easy OAuth using DaliCore and Glassfish: the service consumer

After having completed the first part of this OAuth story, it is now time to implement the second part: the service consumer. While the our service provider made use of the DaliCore module dalicore-oauth, the service consumer will use the module dalicore-externalnetwork to make our connection to the service provider easier.

As in part 1, it is probably best to download the sample service consumer application, which will make this tutorial a bit easier to follow.

Manage service consumers

Before we start with the service consumer application itself, we need to be able to register service consumers in our service provider. Create a new class in the service provider application with the name ServiceConsumerHandler next to the already existing OAuthHandler class.

@Path("serviceconsumer")
@ManagedBean
public class ServiceConsumerHandler {
  @Inject
  OAuthBean oauthBean;

  @GET
  @Produces("text/html")
  public String create(@QueryParam("name") String name) {
    if (name != null && ! name.trim().isEmpty()) {
      DaliServiceConsumer daliServiceConsumer = new DaliServiceConsumer();
      daliServiceConsumer.setName(name);
      daliServiceConsumer.setConsumerKey(StringUtil.getSecureRandomString(16));
      daliServiceConsumer.setConsumerSecret(StringUtil.getSecureRandomString(16));
      daliServiceConsumer.setStatus(DaliServiceConsumer.Status.ACTIVE);
      oauthBean.createDaliServiceConsumer(daliServiceConsumer);

      return "consumer key: " + daliServiceConsumer.getKey() + "
consumer secret: " + daliServiceConsumer.getSecret() + ""; } else { return "No name provided. Add ?name=service_consumer_name at the end of the URL."; } } }

After redeploying the application, we can create a service consumer by calling http://localhost:8080/oauthprovider/rest/serviceconsumer?name=ServiceConsumerName. The response will show us the service consumer key and secret that we will need later on when connecting to the service provider.

Creating the service consumer application

Now we can start with the creation of our oauth service consumer. Create a new Maven Web Application project in NetBeans.

Create a new Maven Web Application project in NetBeans

Maven setup

As in the first part of the series, we need to change the maven pom.xml file so that it will be able to download the LodgON artifacts. Include the following repository in your pom.xml:

<repositories>
  <repository>
    <id>lodgon</id>
    <url>http://maven.lodgon.com/maven2</url>
  </repository>
</repositories>

Afterwards add the following maven dependencies:

<dependency>
  <groupId>com.lodgon.dalicore</groupId>
  <artifactId>dalicore-externalnetwork</artifactId>
  <version>0.9.11-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-server</artifactId>
  <version>1.8</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.sun.jersey.contribs.jersey-oauth</groupId>
  <artifactId>oauth-signature</artifactId>
  <version>1.8</version>
</dependency>

Jersey handler and jsp's

Now we can create a handler that will handle the connection to the service provider. Create a new class called HomeHandler with the following code:

@Path("home")
public class HomeHandler {
  private static final String PROVIDER_URL = "http://localhost:8080/oauthprovider/rest";
  private static final String CONSUMER_KEY = "";
  private static final String CONSUMER_SECRET = "";
  private static final String CONSUMER_CALLBACK = "http://localhost:8080/oauthconsumer/rest/home/callback";

  @Context
  HttpServletRequest request;

  @GET
  public Response home() throws URISyntaxException {
    ExternalToken accessToken = (ExternalToken) request.getSession().getAttribute("accessToken");
    if (accessToken == null) {
      DaliCoreExternalNetwork externalNetwork = new DaliCoreExternalNetwork(PROVIDER_URL,
            CONSUMER_KEY, CONSUMER_SECRET);
      return externalNetwork.connect(CONSUMER_CALLBACK);
    } else {
      return Response.ok(new Viewable("/home.jsp", accessToken)).build();
    }
  }

  @GET
  @Path("callback")
  public Response callback(@QueryParam("oauth_token") String requestToken,
          @QueryParam("oauth_verifier") String verifier) throws URISyntaxException {
    DaliCoreExternalNetwork externalNetwork = new DaliCoreExternalNetwork(PROVIDER_URL,
            CONSUMER_KEY, CONSUMER_SECRET);
    ExternalToken accessToken = externalNetwork.callback(requestToken, verifier);
    request.getSession().setAttribute("accessToken", accessToken);
    return Response.seeOther(new URI("/home")).build();
  }
}

This class contains two entry points. The home entry point is the URL that is being called to initiate a connection with our service provider. It detects if an access token exists on the active session. If an access token does not exist, we will connect with the dalicore-oauth service provider, by using the utility class DaliCoreExternalNetwork. The constructor of this class takes three parameters: the URL to the service provider, our consumer key and our consumer secret. Update the static fields at the top with the consumer key and secret you received by creating a service consumer as mentioned previously. When we have an instance of the DaliCoreExternalNetwork we simply call the connect method and provide the callback where the user will be redirected to after it authorized the service consumer.

The callback is handled by the second entry point, named callback. It accepts the request token and a verifier that we will need to request our access token. This can also be done through the DaliCoreExternalNetwork helper class. By calling the callback method we will finally receive a valid access token which we store on the active session. Finally, we redirect the user back to the home page, which will print our access token information in the browser.

web.xml configuration

Next we need to activate Jersey so our handler will be processed. Create a Standard Deployment Descriptor and put in the following code:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
  <servlet>
    <servlet-name>jerseyservlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>jerseyservlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
</web-app>

JPA Configuration

Finally, we need to configure a connection with the database where the tokens will be stored. Create a persistence.xml file in the directory src/main/resources/META-INF with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="oauth_provider_pu" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/oauthconsumerdb</jta-data-source>
    <class>com.lodgon.dali.core.entity.Application</class>
    <class>com.lodgon.dali.core.entity.Content</class>
    <class>com.lodgon.dali.core.entity.ContentPermission</class>
    <class>com.lodgon.dali.core.entity.Field</class>
    <class>com.lodgon.dali.core.entity.Group</class>
    <class>com.lodgon.dali.core.entity.GroupPermission</class>
    <class>com.lodgon.dali.core.entity.TypePermission</class>
    <class>com.lodgon.dali.core.entity.User</class>
    <class>com.lodgon.dali.core.entity.UserPermission</class>
    <class>com.lodgon.dali.core.externalnetwork.entity.ExternalToken</class>
    <class>com.lodgon.dali.core.externalnetwork.entity.OnlineAccount</class>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Pre-deployment configuration

Again, just like we did when setting up the service provider, we need to add the JDBC resources to glassfish. Create a file called sun-resources.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd">
<resources>
  <jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource" name="derby_oauthconsumerdb_j2eepool" wrap-jdbc-objects="false">
    <property name="ConnectionAttributes" value=";create=true" />
    <property name="DatabaseName" value="memory:oauthconsumerdb" />
  </jdbc-connection-pool>
  <jdbc-resource enabled="true" jndi-name="jdbc/oauthconsumerdb" object-type="user" pool-name="derby_oauthconsumerdb_j2eepool"/>
</resources>

And add the resources to glassfish by calling the add-resources asadmin command:

$ asadmin add-resources sun-resources.xml

Building and deployment

Finally, you can build and deploy the application by pressing F6. Afterwards point your browser to http://localhost:8080/oauthconsumer/rest/home. Login with any username and password, authorize the service consumer and you should have your access token!

Easy OAuth using DaliCore and Glassfish: the service provider

DaliCore is really starting to get some momentum recently, so I thought it is time to write something about it. DaliCore currently contains three modules:
dalicore-ejb
contains everything related to managing the dalicore entities: User, Content, Group and Permission
dalicore-oauth
allows web applications to become an oauth service provider
dalicore-externalnetwork
allows web applications to setup a connection with oauth providers

This blog entry will focus on the second module. I'll show you how easy it is with dalicore-oauth to create a web application that can serve as an oauth service provider. In the next blog entry, we'll make a second web application that serves as the oauth service consumer that will use dalicore-externalnetwork to connect to our service provider. Our application server of choice is glassfish 3.1.1 and we'll be using Jersey for our web related components.

It's probably better to download the sample service provider application already. This should make it easier to follow all the steps below.

Creating an application

Allright, time to get started. We start with creating the web application that will act as our oauth service provider. Fire up NetBeans and create a new Maven Web Application project.

Create a new Maven Web Application project in NetBeans

Maven setup

To be able to make use of dalicore-oauth, we need to change the pom.xml and add LodgOn's maven repository. Include the following code in your pom.xml:

<repositories>
  <repository>
    <id>lodgon</id>
    <url>http://maven.lodgon.com/maven2</url>
  </repository>
</repositories>

Now we can add the maven dependency to dalicore-oauth and other modules that we will need. Add the following dependencies in your pom.xml, in the dependencies section:

<dependency>
  <groupId>com.lodgon.dalicore</groupId>
  <artifactId>dalicore-ejb</artifactId>
  <version>0.9.11-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.lodgon.dalicore</groupId>
  <artifactId>dalicore-oauth</artifactId>
  <version>0.9.11-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-server</artifactId>
  <version>1.8</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.sun.jersey.contribs.jersey-oauth</groupId>
  <artifactId>oauth-signature</artifactId>
  <version>1.8</version>
</dependency>

Jersey handler and jsp's

An oauth service provider typically needs to provide three end points: one for requesting a request token, one to authenticate a user and a third to request an access token. The creation of the request and access tokens are automatically handled by dalicore-oauth. So, the only thing we need to add is the handling of user authentication. Create a new class called OAuthHandler with the following code:

@Path("oauth")
@ManagedBean
public class OAuthHandler {
  @Inject
  OAuthBean oauthBean;
  @Inject
  UserBean userBean;

  @GET
  @Path("authenticate")
  public Viewable authenticate(@QueryParam("oauth_token") String oauthToken) {
    return new Viewable("/login.jsp", new Model(oauthToken));
  }

  @POST
  @Path("login")
  public Viewable login(@FormParam("oauth_token") String oauthToken,
          @FormParam("username") String userName,
          @FormParam("password") String password) throws DaliCoreException {
    User user = userBean.getByUserId(userName);
    if (user == null) {
      user = new OAuthUser();
      user.setUserId(userName);
      user = userBean.create(user);
      userBean.setPassword(user.getId(), password);
    }

    if (userBean.validatePassword(user.getId(), password)) {
      DaliToken requestToken = oauthBean.findDaliToken(oauthToken, DaliToken.Type.OAUTH_REQUEST_TOKEN);
      return new Viewable("/authorize.jsp", new Model(oauthToken, user, oauthToken.getDaliServiceConsumer()));
    } else {
      return new Viewable("/login.jsp", new Model(oauthToken, "Login failed."));
    }
  }

  @POST
  @Path("authorize")
  public Response authorize(@FormParam("oauth_token") String oauthToken,
          @FormParam("userUid") String userUid,
          @DefaultValue("no") @FormParam("authorized") String authorized) throws DaliCoreException, URISyntaxException, UnsupportedEncodingException {
    DaliToken requestToken = oauthBean.findDaliToken(oauthToken, DaliToken.Type.OAUTH_REQUEST_TOKEN);

    if ("yes".equals(authorized)) {
      User user = userBean.getByUid(userUid);
      if (user != null) {
        String verifier = oauthBean.authorizeServiceConsumer(user.getId(), oauthToken, true);
        return Response.seeOther(new URI(requestToken.getCallback() + "?" + OAuthParameters.TOKEN + "=" + URLEncoder.encode(oauthToken, "UTF-8") + "&" + OAuthParameters.VERIFIER + "=" + URLEncoder.encode(verifier, "UTF-8"))).build();
      } else {
        return Response.ok(new Viewable("/login.jsp", new Model(oauthToken))).build();
      }
    } else {
      return Response.seeOther(new URI(requestToken.getCallback())).build();
    }
  }

  public static class Model {
    private String message;
    private String oauthToken;
    private DaliServiceConsumer serviceConsumer;
    private User user;

    public Model(String oauthToken) {
      this.oauthToken = oauthToken;
    }

    public Model(String oauthToken, String message) {
      this.oauthToken = oauthToken;
      this.message = message;
    }

    public Model(String oauthToken, User user, DaliServiceConsumer serviceConsumer) {
      this.oauthToken = oauthToken;
      this.user = user;
      this.serviceConsumer = serviceConsumer;
    }

    public String getMessage() {
      return message;
    }

    public void setMessage(String message) {
      this.message = message;
    }

    public String getOauthToken() {
      return oauthToken;
    }

    public void setOauthToken(String oauthToken) {
      this.oauthToken = oauthToken;
    }

    public DaliServiceConsumer getServiceConsumer() {
      return serviceConsumer;
    }

    public void setServiceConsumer(DaliServiceConsumer serviceConsumer) {
      this.serviceConsumer = serviceConsumer;
    }

    public User getUser() {
      return user;
    }

    public void setUser(User user) {
      this.user = user;
    }
  }
}

The class is a Jersey handler that contains three endpoints. The first endpoint authenticate will be called by service consumers that would like to authenticate a user for their application. We will show a basic login page where a user can provide a username and password.

The second endpoint login handles a user that tries to login. For the sake of simplicity, we will create a new user when no user was found with the provided username. Notice, that this user must be of type OAuthUser, which is an extension of the dalicore-ejb User. Upon successful login, the user will be redirected to an authorization page where he/she can choose whether to allow the service consumer to access his/her user data.

Which brings us to the third endpoint authorize, where a user authorizes the service consumer. When the user allows access to the service consumer, we will call the service consumer's callback. A service consumer should have provided this callback when it requested the request token earlier on in the oauth process. We will come back to that when we create the service consumer application.

Below you will find the two jsp's (login.jsp and authorize.jsp) that we will need, one to allow a user to login and another one to authorize a specific service consumer.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>dalicore-oauth service provider</title>
  </head>
  <body>
    <h1>Authenticate</h1>

    <form method="POST" action="${pageContext.request.contextPath}/rest/oauth/login">
      <input type="hidden" name="oauth_token" value="${it.oauthToken}"/>
      username: <input type="text" name="username"/><br/>
      password: <input type="password" name="password"/><br/>
      <input type="submit" value="login"/>
    </form>
  </body>
</html>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>dalicore-oauth service provider</title>
  </head>
  <body>
    <h1>Authorize</h1>

    <h4>Do you want to allow the service consumer <b>${it.serviceConsumer.name}</b> to access your data:</h4>

    <form method="POST" action="${pageContext.request.contextPath}/rest/oauth/authorize">
      <input type="hidden" name="oauth_token" value="${it.oauthToken}"/>
      <input type="hidden" name="userUid" value="${it.user.uid}"/>
      <input type="submit" name="authorize" value="yes"/>
      <input type="submit" name="authorize" value="no"/>
    </form>
  </body>
</html>

CDI configuration

You probably noticed that at the top of the OAuthHandler class, we use CDI to inject a reference to the UserBean and the OAuthBean. In order for CDI to work, we need to add a beans.xml in the WEB-INF directory. So, create the directory WEB-INF in the Web Pages directory and add the following beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

web.xml configuration

The next steps are required to allow the dalicore-oauth module to be plugged in correctly. We need to configure the Jersey servlet so it can handle oauth requests. This is all done in the standard deployment descriptor (probably better known as web.xml). In NetBeans you can right-click on Web Pages and choose "New -> Standard Deployment Descriptor (web.xml)". In the web.xml file we will add the section for the jersey servlet:

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0">
  <servlet>
    <servlet-name>jerseyservlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.oauth.ignorePathPattern</param-name>
      <param-value>requestToken|accessToken</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.sun.jersey.oauth.server.api.resources;com.lodgon.dali.core.oauth;com.mycompany.oauthprovider</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>jerseyservlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
</web-app>

There are two init parameters for the Jersey servlet that require some explanation. When a URL on our web application is called with the Authorization HTTP header, it will be filtered by the Jersey OAuth filter. However, getting a request token or access token also contains the Authorization HTTP header. Since these aren't regular OAuth requests, these should be ignored by the OAuth filter. This can be done by specifying the com.sun.jersey.config.property.oauth.ignorePathPattern init parameter.

The second init parameter com.sun.jersey.config.property.packages is required to tell Jersey which packages to scan for providers and handlers. Be sure not to forget to specify the package in which you created the OAuthHandler class. Otherwise it won't be detected by Jersey and you'll receive 404 responses when trying to authenticate a user.

JPA configuration

The final requirement is to configure our connection to the database. This is needed because dalicore-oauth persistently stores its request and access tokens, its users and its list of service consumers. Create a persistence.xml file in the directory src/main/resources/META-INF with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="oauth_provider_pu" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/oauthproviderdb</jta-data-source>
    <class>com.lodgon.dali.core.entity.Application</class>
    <class>com.lodgon.dali.core.entity.Content</class>
    <class>com.lodgon.dali.core.entity.ContentPermission</class>
    <class>com.lodgon.dali.core.entity.Field</class>
    <class>com.lodgon.dali.core.entity.Group</class>
    <class>com.lodgon.dali.core.entity.GroupPermission</class>
    <class>com.lodgon.dali.core.entity.TypePermission</class>
    <class>com.lodgon.dali.core.entity.User</class>
    <class>com.lodgon.dali.core.entity.UserPermission</class>
    <class>com.lodgon.dali.core.oauth.entity.Attribute</class>
    <class>com.lodgon.dali.core.oauth.entity.DaliToken</class>
    <class>com.lodgon.dali.core.oauth.entity.DaliServiceConsumer</class>
    <class>com.lodgon.dali.core.oauth.entity.OAuthUser</class>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Pre-deployment configuration

We're almost there. Before we can actually deploy our application, we need to add the JDBC resources to glassfish. Create a file called sun-resources.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd">
<resources>
  <jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource" name="derby_oauthproviderdb_j2eepool" wrap-jdbc-objects="false">
    <property name="ConnectionAttributes" value=";create=true" />
    <property name="DatabaseName" value="memory:oauthproviderdb" />
  </jdbc-connection-pool>
  <jdbc-resource enabled="true" jndi-name="jdbc/oauthproviderdb" object-type="user" pool-name="derby_oauthproviderdb_j2eepool"/>
</resources>

I choose to connect to an in memory derby database here, but you can of course configure your resources to connect to a real database. Add the resources to glassfish by calling the following asadmin command:

$ asadmin add-resources sun-resources.xml

Building and deployment

Now you can build the application and deploy it in glassfish. Building can be done in NetBeans by pressing F11 and deploying is done with the shortkey F6. You can of course also use the command line if you prefer that:

$ mvn clean install
$ asadmin deploy --name oauthprovider --contextroot oauthprovider target/oauthprovider-1.0-SNAPSHOT.war

The application should deploy successfully and now you can point your browser to the following location: http://localhost:8080/oauthprovider/rest/oauth/authenticate. This should show the login screen.

Now it's time to head over to part 2, where we will create the service consumer side that will connect to the service provider application we created here.