In the previous blog post, we discussed about Identity propagation from Java Cloud Service - SaaS Extension (JCS-SX) to Document Cloud Service (DCS) using SAML token.  Here we will explore achieving Identity Propagation by using OAuth 2.0 tokens.

 

To know more about OAuth 2.0 specification, go here.

 

Broadly the architecture of the interaction between JCS-SX, DCS and SIM is as follows:

architecture_oauth.png

 

There are 4 roles in OAuth 2.0 :

Client - the client application that is requesting resources.  In our use case - this would be the JCS-SX Servlet

Resource Owner - the person/entity on whose behalf the resources are being requested.  This is the end user who is logging in.

Resource Server - the server that hosts the protected resources.  This is the Document Cloud Service.

Authorization (OAuth) Server - the server that authenticates the user and issues tokens to the client - this is the Shared Identity Management (SIM)

 

 

The SIM and OWSM enable OAuth token generation and validation seamlessly in Oracle Public Cloud environment.  The pre-requisites for these are

1) Registering a client with the OAuth server - particularly having the client_id and client_secret

2) Registering what resource do we need to access for this client

3) Importing the OAuth signing certificates into the trust store of the Resource Server

4) Generating a client signing certificate and importing it to the Oauth Server.

 

These steps are automatically done during cloud provisioning for different Cloud Services in the same identity domain.  For e.g. in the identity domain having JCS-SX and Document Cloud Service, if you go to the OAuth administration tab, you can see the following already created.

oauth_tab.png

 

Make sure that the resource is accessible to the JCS SX OAuth Client.  You can check list by clicking on the "Modify OAuth Client" option available and see if the same is ticked as below.

modify_oauth_client.png

 

The next step is to create a CSF key for the map "oracle.wsm.security" with the client_id and client_secret corresponding to the JCS-SX.  For this you need to have the JCS-SX SDK (go here for download ) installed.  Replace the values in angular brackets with the actual values.   The credentials here should be of a user who has the Identity Domain Administrator role.

 

set SDK_LIB={location where JCS-SDK is installed}/lib
java -jar %SDK_LIB%/javacloud.jar -user {user} -password {pass} -identitydomain {id-domain} -serviceinstance {sc-name} -datacenter {dc} -set-credential -map oracle.wsm.security -key myoauth.csf.key -keyuser 01b410bc-3b63-4804-9728-c1c64d493526 -keypassword  aUyQltthbCkBzgTZlWw7

 

Next change the JCS-SX Servlet to use the oracle/http_oauth2_token_over_ssl_client_policy.  This policy mandates that the oracle/oauth2_config_client_policy should also be attached. The full code is given below.  Replace example.documents.us.oraclecloud.com with the documents instance URL and example.identity.us.oraclecloud.com with the SIM URL.

 

package com.oracle.rest;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.ClientFilter;
import com.sun.jersey.api.client.filter.LoggingFilter;
import com.sun.jersey.client.urlconnection.HTTPSProperties;

import java.io.IOException;
import java.io.PrintWriter;

import java.security.NoSuchAlgorithmException;

import java.util.Map;

import javax.net.ssl.SSLContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oracle.wsm.agent.handler.jaxrs.RESTClientFilter;
import oracle.wsm.metadata.feature.AbstractPolicyFeature;
import oracle.wsm.metadata.feature.PolicyReferenceFeature;
import oracle.wsm.metadata.feature.PolicySetFeature;
import oracle.wsm.metadata.feature.PropertyFeature;

public class DocsOAuthServlet extends HttpServlet {

    private static final String ENDPOINT_URL =
        "https://example.documents.us.oraclecloud.com/documents/api/1.1/folders/self";
    private static final String CLIENT_CSF_KEY = "myoauth.csf.key";
    private static final String OAUTH_TOKEN_ENDPOINT =
        "https://example.identity.us.oraclecloud.com/oam/oauth2/tokens";


    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
                                                                                          IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        try {
            ClientConfig cc = new DefaultClientConfig();
            Map<String, Object> properties = cc.getProperties();
            SSLContext sslContext = null;
            try {
                sslContext = SSLContext.getInstance("TLS");
            } catch (NoSuchAlgorithmException e) {
                out.println("Exception in setting sslContext is : " + e + " exc messaage is: " + e.getMessage());
            }


            properties.put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(null, sslContext));
            //Adding the policy - starts
            PropertyFeature[] oauthClientPropFeature = new PropertyFeature[1];
            oauthClientPropFeature[0] = new PropertyFeature("oauth2.client.csf.key", CLIENT_CSF_KEY);
            PropertyFeature oauthGeneralPropFeature = new PropertyFeature("token.uri",OAUTH_TOKEN_ENDPOINT);
            
            PolicyReferenceFeature[] oauthPolicyFeature = new PolicyReferenceFeature[] {
                new PolicyReferenceFeature("oracle/oauth2_config_client_policy", oauthGeneralPropFeature ),
                new PolicyReferenceFeature("oracle/http_oauth2_token_over_ssl_client_policy", oauthClientPropFeature)
            };
            properties.put(AbstractPolicyFeature.ABSTRACT_POLICY_FEATURE, new PolicySetFeature(oauthPolicyFeature));
            //Adding the policy - finishes
            
            Client client = Client.create(cc);


            //Adding the client filter - starts
            ClientFilter filter = new RESTClientFilter();
            client.addFilter(new LoggingFilter(System.out));
            client.addFilter(filter);
            //Adding the client filter - finishes
          

            WebResource resource = client.resource(ENDPOINT_URL);
            ClientResponse clientResponse = resource.get(ClientResponse.class);
            String output = clientResponse.getEntity(String.class);
            out.println("\n\n<br>----------Output starts--------------");
            out.println("<br/>Response body from Service is : " + output);
            out.println("<br/>HTTP Status : " + clientResponse.getStatus());
            out.println("<br/>--------------Output ends----------------");
        } catch (Exception e) {
            out.println("Exception is : " + e + " exc messaage is: " + e.getMessage());
        }
        out.println("</body></html>");
    }


    public void destroy() {
        super.destroy();
    }
}

 

In the web.xml we add another servlet entry corresponding to DocsOAuthServlet as below:

<?xml version = '1.0' encoding = 'windows-1252'?>
<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_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>Servlet1</servlet-name>
        <servlet-class>com.oracle.rest.DocsServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Servlet1</servlet-name>
        <url-pattern>/saml</url-pattern>
    </servlet-mapping>
    
        <servlet>
        <servlet-name>Servlet2</servlet-name>
        <servlet-class>com.oracle.rest.DocsOAuthServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Servlet2</servlet-name>
        <url-pattern>/oauth</url-pattern>
    </servlet-mapping>
    
    
    <security-constraint>
        <display-name>name</display-name>
        <web-resource-collection>
            <web-resource-name>name</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
    </security-constraint>
    <login-config>
        <auth-method>CLIENT-CERT</auth-method>
        <realm-name>default</realm-name>
    </login-config>
</web-app>

 

Accessing the servlet URL should give an output as below:

oauth_output1.png

The servlet will call the DCS REST API with the authorization header "Bearer <oauth token" and display the result in the servlet response.  The process of getting the token is handled by the OWSM agent on the JCS-SX side.

 

Jersey 1.0 provides a LoggingFilter class that can be used for logging requests.  We have attached this filter in our code:

            client.addFilter(new LoggingFilter(System.out));

 

We can check the output of the LoggingFilter from the system logs provided by JCS-SX.  Go to the JCS-SX Console -> View Log Messages

 

oauth_console_log.png

 

Additional information

In case you dont attach the oracle/oauth2_config_client_policy, you will get an exception like the below

oracle.wsm.common.sdk.WSMException: WSM-07620 : Agent cannot enforce policies due to either failure in retrieving polices or error in validations, detail= "WSM-01797 Attachment of an oauth2 client policy without oauth2 config policy is invalid. Ensure you attach a valid oauth2 config policy either via Direct Policy Attachment or via Global Policy Attachment along with an oauth2 client policy. "

 

 

Note that, there is a mandatory property "token-uri" that needs to be provided to the oauth2_config_client_policy.  This is the OAuth 2.0 token endpoint and will typically be <iddomain>.identity.oracle.com/oam/oauth2/tokens.  For more details refer to the OWSM oauth2_config_client_policy documentation.

 

For the http_oauth2_token_over_ssl_client_policy, there is a mandatory property called oauth2.client.csf.key.  This will hold the key name in the oracle.wsm.security map that will store the client_id and client_secret for the JCS-SX client.  If the required key doesnt exist in the oracle.wsm.security map you will get an exception like the below

 

oracle.wsm.common.sdk.WSMException: WSM-00054 : The password credentials cannot be obtained from the Credential Store Framework (CSF). Unable to retrieve the csfKey "myoauth.csf.key". The csf map "oracle.wsm.security" found in the credential store is being used and the following keys are found in this map :- []

 

 

For more details, refer to the OWSM http_oauth2_token_over_ssl_client_policy documentation

 

The views expressed in this post are my own and do not necessarily reflect the views of Oracle.