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!

14 comments:

BeginnersMind said...

Hi, Good post..I am using Dalicore...
I have a problem though..in one of our projects i am usinf JSF on the pront end..Now is it possible to request for a Sign on from a JSF page and consequently is it possible to forward to a JSF page after the Sign on..

Thanks in Advance
Mohit

BeginnersMind said...

Hi I figured out a work around through jsp...I have a couple more questions.

1. Is there a way to get the Social network Link..profile link i mean

2. and google+ and linked in dont send the emails..

Joeri Sykora said...

Hi Mohit,

thanks for trying out DaliCore. Regarding your first question about JSF. This made me curious and I tried replacing our jersey sample with one that uses JSF. You can find the code here: Easy OAuth using DaliCore with JSF

Answering your second comment:

1. We currently don't store a link to the profile page of the external network. But you could always create a feature request in theJIRA issue tracker on the DaliCore project page on java.net (you will need a java.net login for that).

2. I don't think I understand your question. We currently don't send any emails from DaliCore when connecting to one of the supported external networks.

BeginnersMind said...

Thanks i saw the code and i did something similar...:)

Sure i make a request for sending profile links..

What i meant was @Entity User has a get email method that returns a null for Google+ and Linkedin, Facebook returns the email...

I was wondering if this had something to do with oauth request...

Joeri Sykora said...

I don't know which version of DaliCore you are using at the moment? In the latest SNAPSHOT version (1.1.0-SNAPSHOT) we fixed retrieving the email address from a LinkedIn profile some time ago. I also just fixed the same issue for getting the email address from a Google account.

BeginnersMind said...

Hi Joeri,
Great..i was using dalicore-ejb-0.9.11 and dalicore-social-1.1.0-SNAPSHOT...

Thanks a ton for your prompt responses...I had 1 last query..i checked apidocs for the same versions mentioned above..is there a way to share content using using dalicore( for e.g. one has options on various options on website to share the comments one makes after SSO on the signed in network)..

Thanks in advance.
Mohit

BeginnersMind said...

Hi Joeri,
Great..i was using dalicore-ejb-0.9.11 and dalicore-social-1.1.0-SNAPSHOT...

Thanks a ton for your prompt responses...I had 1 last query..i checked apidocs for the same versions mentioned above..is there a way to share content using using dalicore( for e.g. one has options on various options on website to share the comments one makes after SSO on the signed in network)..

Thanks in advance.
Mohit

BeginnersMind said...

hey i checked out I am using dalicore social 1.1.0 and dalicore ejb0.9.11 i can confirm, the email is null..

i did not find dalicore ejb 1.1.0

Thanks anyway
Mohit

BeginnersMind said...

Sorry...the email is not null after i downloaded the source and ran the sample...but with the jar of the same version it is null...

Joeri Sykora said...

Yes, we had a problem with our tests which caused the jars not to be deployed to the maven repository. We fixed this today, so normally the snapshot releases should contain the same as what is in the subversion code now.

Btw, I don't recommend mixing versions up between different components of DaliCore as they normally have a dependency on each other. I mean that dalicore-social-1.1.0-SNAPSHOT will have a dependency on dalicore-ejb-1.1.0-SNAPSHOT, so it is not guaranteed everything will work as expected if you explicitly include a different version.

BeginnersMind said...

I understand the dependancy but could not locate the dalicore-ejb1.1.0 through maven or otherwise..ill try today.

.I had 1 last query..i checked apidocs for the same versions mentioned above..is there a way to share content using using dalicore( for e.g. one has options on various options on website to share the comments one makes after SSO on the signed in network)..

Thank a ton in advance.
Regards
Mohit

Joeri Sykora said...

Hey Mohit, I have difficulty understanding what you are asking in your question.

Do you mean that you want e.g. comments that users make on your website, that these are also published on the various social networks that they signed up with? This is not yet possible, but we created issues for this already, so these are definitely in the pipeline (see following jira issues: DALICORE-52, DALICORE-53 and DALICORE-54).

BeginnersMind said...

that is exactly what i meant and thanks for all the help.

Regards
Mohit

BeginnersMind said...

Hi Joeri,

I am facing another issue. If the social network profile picture is changed dalicore does not update the @Entity User...i tried a couple of work arounds but none of them worked. It keeps returning the older picture. Is there an easy way out.

Thanks in advance
Mohit