Wednesday, July 6, 2011

Integrating 3-legged OAuth with XACML

This blog post explains in detail how to integrate 3-legged OAuth with XACML. Detailed explanations on OAuth and XACML are out of scope.

You need to download following products to get the sample running.

1. WSO2 Identity Server - acting as the service provider, where a user can register an OAuth consumer app to obtain a request token, authorize it and obtain an access token. Also WSO2 Identity Server is used as the XACML engine. Identity Server can be downloaded from here.

2. WSO2 ESB - acting as an interceptor to the final resource the consumer wants to access. ESB will intercept the request and performs OAuth validation. WSO2 ESB can be downloaded from here.

3. WSO2 Application Server - this is used to host WSO2 OAuth Playground web app. App Server can be downloaded from here.

4. Download and unzip the sample from here to OAUTH_SAMPLE.

Let's first start by setting up the WSO2 Identity Server.


1. Unzip the downloaded copy of Identity Server and remove all jars starting with org.wso2.carbon.identity.oauth inside [IS_HOME]\reposiroty\components\plugins

2. Copy following jars from OAUTH_SAMPLE\dropins to [IS_HOME]\reposiroty\components\dropins
org.wso2.carbon.identity.oauth-3.2.1.jar
org.wso2.carbon.identity.oauth.stub-3.2.1.jar
org.wso2.carbon.identity.oauth.ui-3.2.1.jar

3. Above [1] & [2] steps are not required when OAuth 3.2.1 feature is available in WSO2 feature repo - you can simply point there and update.

4. Start WSO2 Identity Server

5. Login as admin/admin and go to OAuth and register a consumer application. When creating the application make sure you give http://localhost:9767/playground/oauth/oauth_callback as the callback url. This is the location we are going to setup our sample web app later.

6. Also - once created the app - click on the link under you application name - there you can see the corresponding consumer key and the consumer secret for your app.


7. Now we need to setup the XACML engine in WSO2 Identity Server

9. Go to Entitlement --> Administration --> Import Policy --> Select file system and import the policy OAUTH_SAMPLE/xacml.policy

10. Once imported - make sure you click on the ENABLE link against the imported policy.

11. That completes the Identity Server setup.

Now, let's setup WSO2 ESB.

1. Unzip the downloaded copy of WSO2 ESB and remove all jars starting with org.wso2.carbon.identity.oauth inside [ESB_HOME]\reposiroty\components\plugins

2. Copy following jars from OAUTH_SAMPLE\dropins to [ESB_HOME]\reposiroty\components\dropins
org.wso2.carbon.identity.oauth.mediator-3.2.1.jar
org.wso2.carbon.identity.oauth.stub-3.2.1.jar

3. Above [1] & [2] steps are not required when OAuth 3.2.1 feature is available in WSO2 feature repo - you can simply point there and update.

4. Open ESB_HOME\repository\conf\carbon.xml and find for the element Offset and set it to 2.
This will make sure that ESB starts on ports 9445/9765.

5. Start WSO2 ESB

6. Login as admin/admin and replace the synapse configuration in the source view - with the content from OAUTH_SAMPLE\synapse.xml

7. That's it and we are done with the ESB.

Let's set up the sample web app with WSO2 Application Server.

1. Open AS_HOME\repository\conf\carbon.xml and find for the element Offset and set it to 4.
This will make sure that App Server starts on ports 9447/9767.

2. Copy OAUTH_SAMPLE\playground.war to AS_HOME\repository\deployment\server\webapps

3. Start WSO2 Application Server

Now let's see how web app works.

1. Go to http://localhost:9767/playground


2. To get the request token, type your consumer key and consumer secret corresponding to your registered oauth application. Set the scope as echoService [should be the exact name, since that is what is been referred in the XACML policy].

3. Once you get the request token, you need to get that authorized by the user.

4. After that you can exchange the authorized token to an access token.

5. Copy the value of oauth_token from the response to [4] - we need this value when we are going to access the resource.

Now let's try to invoke a service with the obtained access token. Following java code explains how to do that. You need replace the values of CONSUMER_SECRET,CONSUMER_KEY and OAUTH_TOKEN. The value of OAUTH_TOKEN is the one you got with the access token.
package org.wso2.rest.security.oauth;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

import com.google.gdata.client.GoogleService;
import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
import com.google.gdata.client.authn.oauth.OAuthHmacSha1Signer;

public class OauthClient {

 private static final String ESB = "http://127.0.0.1:8282/";

 /**
  * @param args
  */
 public static void main(String[] args) {

  final String CONSUMER_SECRET = "9FXwlWNQdUlG2rWk7os3VRz8dfsa";
  final String CONSUMER_KEY = "yumx9s1HAf1iaItr1UZw4afEEjga";
  final String OAUTH_TOKEN = "hfiYq1ppu3VhLsFWxpFjCUDc3Jwa";

  GDataRequest request = null;

  try {

   GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
   oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
   oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
   oauthParameters.setOAuthToken(OAUTH_TOKEN);

   OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();
   GoogleService service = new GoogleService("demoservice", "myapp");
   service.setOAuthCredentials(oauthParameters, signer);
   String param = "hi";
   String baseString = ESB + "services/oauth_proxy/echoString" + "?xoauth_requestor_id="
     + CONSUMER_KEY + "&in=" + param + "&scope=echoService";
   URL feedUrl = new URL(baseString);
   request = service.createFeedRequest(feedUrl);
   request.execute();
   System.out.println(convertStreamToString(request.getResponseStream()));
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 private static String convertStreamToString(InputStream is) throws IOException {
  if (is != null) {
   StringBuilder sb = new StringBuilder();
   String line;
   try {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    while ((line = reader.readLine()) != null) {
     sb.append(line).append("\n");
    }
   } finally {
    is.close();
   }
   return sb.toString();
  } else {
   return "";
  }
 }

}

To run the above client you need to have google-collect and gdata-core jars in your classpath.

7 comments:

Atmaram said...

Thanks for the post. It looks quite interesting. I have setup the playground app in glassfish instead of WSO2 AS. It is not able to get the certificate.
Can you share the source code and explain how to setup the certificate file.

-atmaram

益盛 said...
This comment has been removed by the author.
Yuli said...

I'm trying to use WSO2 Identity server for OAuth token management for my own web application.

I exactly follow the instructions in the blog and downloaded WSO2 identity server 3.2.3, wso2esb 4.0.3, wso2as 4.1.2 in Ubuntu 10.04. I successfully got the access token.

However, when I ran the code in the last part of the blog in Eclipse(I've already used the correct CONSUMER_SECRET,CONSUMER_KEY and OAUTH_TOKEN in the code), I ended up with an error:

java.net.ConnectException: Connection refused at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384) at java.net.Socket.connect(Socket.java:546) at java.net.Socket.connect(Socket.java:495) at sun.net.NetworkClient.doConnect(NetworkClient.java:178) at sun.net.www.http.HttpClient.openServer(HttpClient.java:409) at sun.net.www.http.HttpClient.openServer(HttpClient.java:530) at sun.net.www.http.HttpClient.(HttpClient.java:240) at sun.net.www.http.HttpClient.New(HttpClient.java:321) at sun.net.www.http.HttpClient.New(HttpClient.java:338) at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:935) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:876) at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:801) at com.google.gdata.client.http.HttpGDataRequest.execute(HttpGDataRequest.java:488) at com.google.gdata.client.http.GoogleGDataRequest.execute(GoogleGDataRequest.java:515) at org.wso2.rest.security.oauth.OauthClient.main(OauthClient.java:44)

I'm sure the service port is available and can't figure out what the problem is. Could anyone provide any help?

Besides, in the example in that blog, it seems like I have to connect my own web application to ESB to validate the token. Is there any API I can use in my app to validate the token directly with the Identity Server? Or how should I connect my Jsp web app to the ESB?

Vlad said...
This comment has been removed by the author.
Vlad said...

Can you please share the source code?

Alexandre Martins said...

Great article!
Thanks for that!

I dunno what's happening but I'm struggling to download OAUTH_SAMPLE zip file.

It'd be great if I could download that!

Thanks.
Alex.

Alexandre Martins said...

Great article!
Thanks for that!

I dunno what's happening but I'm struggling to download OAUTH_SAMPLE zip file.

It'd be great if I could download that!

Thanks.
Alex.