Wednesday, December 18, 2013

[Code Snippet] Calling a REST operation protected with OAuth via HTTPClient


private void callRest(HttpServletRequest request, HttpServletResponse response) throws IOException {
 
 PostMethod post = new PostMethod("http://localhost:9764/jaxrs_basic/services/customers/customerservice/customers/name");
 
 post.addRequestHeader("Accept", "text/plain");
 post.addRequestHeader("Authorization", "Bearer " + oauthToken);
 
 try {
  RequestEntity myEntity = new StringRequestEntity("", "text/plain", "ISO-8859-1");
  post.setRequestEntity(myEntity);
  
  HttpClient httpclient = new HttpClient();
  int result = httpclient.executeMethod(post);
  
  System.out.println("Response status code: " + result);
  System.out.println("Response body: ");
  System.out.println(post.getResponseBodyAsString());
  
 } catch (Exception e) {
  e.printStackTrace();
 } finally {
  post.releaseConnection();
 }
}

[Code Snippet] Exchange SAML2 Token to an OAuth Token via HTTPClient & parse JWT using Gson


...

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;

import com.google.gson.Gson;

...

private String exchangeSAMLTokenToOAuthToken(HttpServletRequest request, HttpServletResponse response) throws IOException{
  
 String clientId = properties.getProperty("OAuth.client.id");
 String clientSecret = properties.getProperty("OAuth.client.secret");
 String oauthTokenEndpoint = properties.getProperty("OAuth.token.endpoint");

 HttpClient httpclient = new HttpClient();
 httpclient.getParams().setAuthenticationPreemptive(true);
 Credentials defaultcreds = new UsernamePasswordCredentials(clientId, clientSecret);
 httpclient.getState().setCredentials(AuthScope.ANY, defaultcreds);

 PostMethod post = new PostMethod(oauthTokenEndpoint);
 post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
 post.addParameter("grant_type", "urn:ietf:params:oauth:grant-type:saml2-bearer");
 post.addParameter("assertion", samlAssertion);
  
 try {
  httpclient.executeMethod(post);
  System.out.println("Response from OAuth Token endpoint: ");
  System.out.println(post.getResponseBodyAsString());
  Map jsonJavaRootObject = new Gson().fromJson(post.getResponseBodyAsString(), Map.class);
  return (String)jsonJavaRootObject.get("access_token");
 } catch (Exception e) {
  e.printStackTrace();
 } finally {
  post.releaseConnection();
 }

 return null;
}

Friday, December 13, 2013

Enabling SAML SSO for web apps hosted in WSO2 Application Server via a Tomcat Valve

Usecase

When a user tries to access a web application hosted on WSO2 Application Server (AS) he should be authenticated by WSO2 Identity Server via SAML SSO, and also his roles should be sent back in the SAML Response. Then the web application should be able to get the authentication and authorization details from the request's UserPrincipal object (i.e. principal name and check isUserInRole).

Later, when the user tries to access another web app on the same AS server, he should be auto logged in without presenting a login page.

I have created a sample to demonstrate this usecase. We can set it up and execute by following the below steps.

Configuring the Application Server side

1. Download WSO2 Application Server 4.5.0 [1] and extract it (wso2as-5.2.0.zip).

2. Copy org.wso2.carbon.sample.tomcat.valve.samlsso-1.0.0.jar [bin][src] to wso2as-5.2.0/repository/components/lib

3. Copy org.wso2.carbon.identity.sso.agent-1.0.0.jar [bin][src] to wso2as-5.2.0/repository/components/lib

4. Modify wso2as-5.2.0/repository/conf/tomcat/catalina-server.xml by adding following under "Host" section:
<Valve className="org.wso2.carbon.sample.tomcat.valve.samlsso.SAMLSSOValve"/>

5.  Copy foo-app.war [bin][src] and bar-app.war [bin][src] to wso2as-5.2.0/repository/deployment/server/webapps.

6. Open them in an archive manager and change the "saml2.config.file.path"  context param in /WEB-INF/web.xml to point to the location of the properties file that contains the configurations. By default this file is located in /WEB-INF/classes directory.

7. Also, in those same property files, change the value: KeyStore=[path to]/wso2as-5.2.0/repository/resources/security/wso2carbon.jks

8. Change the port "offset" to "1" in wso2as-5.2.0/repository/conf/carbon.xml.

9. Run the server by executing wso2as-5.2.0/bin/wso2carbon.sh if on a Unix based systems, or /bin/wso2carbon.bat if on Windows.

Configuring the Identity Server

1. Download WSO2 Identity Server 4.5.0 [2] and extract it (wso2is-4.5.0.zip).

2.  Run the server by executing wso2is-4.5.0/bin/wso2carbon.sh if on a Unix based systems, or /bin/wso2carbon.bat if on Windows.

3. On the home page, under Manage section, click "SAML SSO" and then click "Register New Service Provider".

4. Fill following details on the registration page:

* Issuer: foo-app
* Assertion Consumer URL: https://localhost:9444/foo-app/acs
* Select Enable Response Signing
* Select Enable Assertion Signing 
* Select Enable Single Logout
* Select Enable Attribute Profile 
* Add claim: "http://wso2.org/claims/role".
* Select Include Attributes in the Response Always

Finally click "Register".



5. bar-app should be also registered in the same manner. Just replace "bar" with "foo" in the above configurations.



Let's test this...

1. Access foo-app deployed in the AS via the web browser: https://localhost:9444/foo-app/

2. It gets redirected to IS login page. Give "admin":"admin" (username:password) and click "Sign In"


3. Browser gets redirected back to the web app; we have successfully authenticated via SAML SSO.


4. Now try to access https://localhost:9444/bar-app/. Though it gets redirected to IS, login page is not displayed since IS already has a session regarding this user.

User gets successfully authenticated to the bar-app also..

Ref:
[1] http://wso2.com/products/application-server/
[2] http://wso2.com/products/identity-server/

Securing an existing REST service via SAML 2.0 Bearer Assertion Grant type

Usecase

A web application is secured with SAML SSO using WSO2 Identity Server 4.5.0 (IS) as the SAMLSSO provider.

After authenticating with SAMLSSO, user tries to access a REST service secured with OAuth, which is hosted in WSO2 Application Server 5.2.0 (AS). But he should not have to reauthenticate.

The web application should be able to exchange the received SAML Token (i.e. SAML Assertion) for an OAuth token, and send it to the REST service, which will then validate it with the IS. 

I have created a sample to demonstrate this usecase. Following is how to try it:

Configuring the WSO2 Identity Server

1. Download WSO2 Identity Server 4.5.0 [1] and extract it (wso2is-4.5.0.zip).

2.In wso2is-4.5.0/repository/conf/identity.xml locate the following config block. Make Enabled=true. By default it's false.

<AuthorizationContextTokenGeneration>        
            <Enabled>true</Enabled>
            <TokenGeneratorImplClass>org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator</TokenGeneratorImplClass>
            <ClaimsRetrieverImplClass>org.wso2.carbon.identity.oauth2.authcontext.DefaultClaimsRetriever</ClaimsRetrieverImplClass>
            <ConsumerDialectURI>http://wso2.org/claims</ConsumerDialectURI>
            <SignatureAlgorithm>SHA256withRSA</SignatureAlgorithm>
            <AuthorizationContextTTL>15</AuthorizationContextTTL>
        </AuthorizationContextTokenGeneration>

3. Run the server by executing wso2is-4.5.0/bin/wso2carbon.sh if on a Unix based systems, or /bin/wso2carbon.bat if on Windows.

4. Access the server by giving https://localhost:9443/carbon on your web browser. And log in as "admin":"admin"(username:password)

SAMLSSO Configuration...

5. On the home page, under Manage section, click "SAML SSO" and then click "Register New Service Provider".

6. Fill following details on the registration page:

* Issuer: SSOSampleApp
* Assertion Consumer URL: http://localhost:8080/SSOSampleApp/acs
* Select Enable Response Signing 
* Select Enable Assertion Signing 

* Select Enable Single Logout.
* Select Enable Audience Restriction
* Add Audience "https://localhost:9443/oauth2/token"

Finally click "Register".


OAuth Configuration...

7. Click OAuth under Manage section (right after SAMLSSO), then click "Register New Application"

8. Fill the form with following:

Application Name: SSOSampleApp
Unselect Code, Implicit, Password, Client Credential, Refresh Token

Finally click "Add"


9. Some settings of this new application will be needed when configuring our sample web application. Click "SSOSampleApp" on the OAuth page, you will be presented with the "Application Settings", copy Client Id, Client Secret.


Trusted Identity Providers Configuration...

10. Click "Trusted Identity Providers" (in the "Configure tab") and then click "Add New Trusted Identity Provider"

11. Fill the details as follows:

Identity Provider Name: wso2is
Identity Provider Issuer: https://localhost:9443/samlsso
Identity Provider Url: https://localhost:9443
Identity Provider Public Certificate: upload certificate

To create the public certificate, go to wso2is-4.5.0/repository/resources/security and execute following command.

keytool -export -alias wso2carbon -file wso2carbon.crt -keystore wso2carbon.jks -storepass wso2carbon



Configuring the Application Server

1. Download WSO2 Application Server 4.5.0 [2] and extract it (wso2as-5.2.0.zip)

I have used a sample that comes packed with AS as the existing REST service. Let's follow below steps to deploy it:

2. Go to wso2as-5.2.0/samples/Jaxws-Jaxrs/jaxrs_basic. README in that folder explains what this app does. I'll be using the /customers/name/ operation, which will return the hard-coded value "Isuru Suriarachchi".

3. Run "ant". This will deploy the jaxrs_basic app in WSO2 AS at wso2as-5.2.0/repository/deployment/server/webapps/

4. Copy org.wso2.carbon.sample.oauth.filter-1.0.0.jar [bin][src] to wso2as-5.2.0/repository/components/lib/

5. Copy org.wso2.carbon.identity.oauth.stub_4.2.0.jar [bin] to wso2as-5.2.0/repository/components/dropins/

6. Add following filter to wso2as-5.2.0/repository/deployment/server/webapps/jaxrs_basic/WEB-INF/web.xml

<filter>
        <filter-name>OAuthValidationFilter</filter-name>
        <filter-class>org.wso2.carbon.sample.oauth.filter.OAuthTokenValidatorFilter</filter-class>
       
        <init-param>
         <param-name>Oauth2TokenValidationServiceURL</param-name>
         <param-value>https://localhost:9443/services/OAuth2TokenValidationService</param-value>
        </init-param>
       
        <init-param>
         <param-name>IdPUsername</param-name>
         <param-value>admin</param-value>
        </init-param>
       
        <init-param>
         <param-name>IdPPassword</param-name>
         <param-value>admin</param-value>
        </init-param>
    </filter>
   
    <filter-mapping>
        <filter-name>OAuthValidationFilter</filter-name>
        <url-pattern>/services/*</url-pattern>
    </filter-mapping>

7. Change the port "offset" to "1" in wso2as-5.2.0/repository/conf/carbon.xml.

8. Finally, run the server as we did with IS, using the wso2server.sh/bat script inside the bin folder.

Configuring the Sample Web App

1. Open SSOSampleApp.war [bin][src] using an archive manager and change following properties located in /WEB-INF/classes/sample.properties

KeyStore=[path to]/wso2as-5.2.0/repository/resources/security/wso2carbon.jks

ConsumerUrl=http://localhost:8080/SSOSampleApp/acs
(Change the above URL according to the application server you are using. This the hostname:port of the Tomcat instance I'm running)

OAuth.client.id=[Client id you copied at step 9 of configuring the IS]
OAuth.client.secret=[Client secret you copied at step 9 of configuring the IS]

2 . Deploy SSOSampleApp.war in your application server and run the server.

Let's test this...
1. Access the webapp deployed in the application server via the web browser: localhost:8080/SSOSampleApp/

2. It gets redirected to IS login page. Give "admin":"admin" (username:password) and click "Sign In"


Browser get redirected back to the web app. Now we have successfully authenticated via SAML SSO.



To do the REST call to the sample app we deployed in AS... Click "here".

You'll see the response as Isuru Suriarachchi.


Tomcat console will display the OAuth token received when we exchanged the SAML token.


AS console will display the result of the OAuth token validation.


Ref:
[1] http://wso2.com/products/identity-server/
[2] http://wso2.com/products/application-server/