Skip to Content

Introduction

OAuth 2.0 is becoming a common authentication method to access REST-based services (i.e. Concur, Google, SFDC). Unfortunately, different organizations might have different implementations of OAuth 2.0 and thus there is lack of a standard approach to it. According to the blog Authenticating from HANA Cloud Integration, HCI currently only supports the Client Credentials Grant Type for OAuth 2.0.

Unlike PI, fortunately HCI has a flexible pipeline processing based on Apache Camel and this allows for easy inclusion of intermediate request-reply calls before the message reaches the final target.

In this blog, I will share how we can take advantage of HCI’s flexible iFlow model to design a solution using custom OAuth 2.0 authentication that has yet to be supported by HCI natively. For the example, I will implement Concur’s OAuth 2.0 Native Authorization Flow to retrieve an access token prior to accessing Concur’s REST API. This is analogous to Option 3 of the following article detailing the support for Concur’s OAuth 2.0 in PI’s REST adapter.

PI REST Adapter – Connect to Concur

Component Details

As HCI is a cloud solution with automatic rolling updates, these steps are valid for the following versions and may change in future updates.

Below are component versions of the tenant and Eclipse plugins.

HCI Tenant Version: 2.8.5

Eclipse Plugin Versions: Adapter 2.11.1, Designer 2.11.1, Operations 2.10.0

iFlow Design

Below is the design of the iFlow for this example. For simplicity sake, it uses a start timer to trigger the flow once it is deployed. The target recipient is an HTTP server that logs the message posted to it.

/wp-content/uploads/2015/12/oauth_iflow_854235.png

Following is an overview of the various sections involved in the flow, which will be elaborated further.

  • Step 1 – Retrieve the user credentials and execute call to Concur token endpoint to retrieve the token
  • Step 2 – Extract the access token value from response of previous call and formulate the HTTP header for authentication using OAuth
  • Step 3 – Execute call to Concur REST API
  • Step 4 – Send REST API response to HTTP target

Design & Configuration

In this section, I will elaborate further on the design and configuration of each step.

Step 1

In order to retrieve an OAuth 2.0 access token from Concur, we first need to perform an HTTP GET call to its token endpoint. This token endpoint expects basic authentication which consists of the Base64 encoded value of the credential (userID:password) in the HTTP header. Additionally, the consumer key needs to be also passed in the HTTP header.

In order to achieve this, we first make use of the User Credentials artifact to securely store and deploy the user ID and password. For more details on that, refer to the following blog on how to configure and deploy the artifact. For this example, I have deployed the credentials under the artifact name ConcurLogin.

Building your first iFlow – Part 4: Configuring your credentials

Within the iFlow, this credential will be retrieved using a Groovy script by utilizing the SecureStoreService API.

/wp-content/uploads/2015/12/script1_854267.png

Below is the logic for the Groovy script. Basically it retrieves the user credential from artifact ConcurLogin. It then encodes the login credentials in Base64. Finally it sets the two required HTTP header fields (note that I have deliberately modified the actual value of the consumer key).


import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import javax.xml.bind.DatatypeConverter;
import com.sap.it.api.ITApiFactory;
import com.sap.it.api.securestore.SecureStoreService;
import com.sap.it.api.securestore.UserCredential;
def Message processData(Message message) {
  def service = ITApiFactory.getApi(SecureStoreService.class, null);
  def credential = service.getUserCredential("ConcurLogin");
  if (credential == null){
    throw new IllegalStateException("No credential found for alias 'ConcurLogin'");
  }
  String user = credential.getUsername();
  String password = new String(credential.getPassword());
  def credentials = user + ":" + password;
  def byteContent = credentials.getBytes("UTF-8");
  // Construct the login authorization in Base64
  def auth = DatatypeConverter.printBase64Binary(byteContent);
  message.setHeader("Authorization", "Basic " + auth);
  message.setHeader("X-ConsumerKey", "xxxx");
  return message;
}





Subsequently, the message is processed by a Request-Reply step that calls the token endpoint using the following channel configuration.

/wp-content/uploads/2015/12/token_channel_854276.png

Step 2

After the call to the token endpoint, the token is provided in an XML response as shown below.

/wp-content/uploads/2015/12/token_854277.png

We will then use a Content Modifier step to extract the token value via XPath and store it in a property.

/wp-content/uploads/2015/12/extract_854278.png

To access Concur’s REST API, it requires the OAuth token to be specified in the HTTP header as shown below:-

Authorization: OAuth <token_value>

Once we have the token value, it will be used in the following Groovy script logic to set the HTTP header.


import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
  // Get the OAuth token value from the properties
  def map = message.getProperties();
  def token = map.get("token");
  // Remove the Consumer key from previous header
  map = message.getHeaders();
  map.remove("X-ConsumerKey");
  // Set the OAuth authorization credentials
  message.setHeader("Authorization", "OAuth " + token);
  return message;
}



Step 3

Once the OAuth token has been set in the HTTP header, the subsequent call to Concur’s REST API is configured as an HTTP receiver channel. In the example below, I configure the channel to use Concur’s Extract API to retrieve an extract file.

/wp-content/uploads/2015/12/restchannel_854288.png

Step 4

Finally, the response of the REST API call is sent to a target HTTP logging system. For the example, I have used the tool described in the following blow.

Testing: Test Tools…Part 1 *HTTP *

Testing Results

Once the iFlow has been completed and deployed, the interface will be triggered and the outcome can be seen on the POST Test Server log. As shown below, we were able to retrieve the extract file using Concur’s Extract API utilizing a custom OAuth 2.0 authentication.

/wp-content/uploads/2015/12/output_854290.png

Conclusion

As shown above, HCI’s flexible pipeline allows us to built quite complex iFlows. Together with custom Groovy scripts, it allows for a lot of possibility to design a customized solution. In this example, I have shown how it is utilized to built a custom OAuth 2.0 authentication that has not been supported by HCI natively. These days, more and more services are using two-step approaches involving tokens and session IDs, and as such having such a flexible pipeline comes in very handy to tackle those integration requirements.

To report this post you need to login first.

12 Comments

You must be Logged on to comment or reply to a post.

  1. Lavanya Musuvadhi Mothilal

    Hi Eng Swee,

    1. You have used Basic authentication in step1 but in the receiver channel, the authentication is set to “None”

    2. Similarly, you used OAuth authentication in Step 3 but the authentication is set to “None” in the receiver channel

    How does it work in spite of setting the authentication to None ?

    Thanks,

    Lavanya

    (0) 
    1. Eng Swee Yeoh Post author

      Hi Lavanya

      The standard authentication function in the receiver channels are not sufficient to achieve login for both steps. Therefore authentication for both channels are set to None, and actually authentication is achieved by setting the required HTTP headers using Groovy script.

      Regards

      Eng Swee

      (0) 
  2. Amol Devidas Badgujar

    Hello,

    With first request to fetch / refresh token from Concur i am getting below error.I followed same steps mentioned above. Could you please help me here.

    Error               = javax.net.ssl.SSLHandshakeException: General SSLEngine problem, cause: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

                                          Sent To URI         = direct://ConcurtoSFTP_TimerEventDefinition_1

    Rgds,

    -Amol

    (0) 
  3. Harsh Bhatt

    Hi Eng Swee Yeoh,

     

    Nicely explained. Thank you for sharing.

    Well, I have similar question as Alexey’s above. Has anything changed in 2017 regarding HCI supporting Oauth 2.0?

     

    Best Regards,

    Harsh

     

    (1) 
  4. Beverely Parks

    Hi Eng Swee Yeoh,

    First, thank you for this blog as it has been very helpful. ¬†I’m hoping that you can help me find a solution to a issue that I’m encountering. ¬† I am needing to perform a token request in JSON format. ¬†Currently, I am using a Content Modifier to hard code the client_id and client_secret into the XML body and then I am using the XML to JSON Converter. ¬†My current road block is that our client secret contains the some characters <n/ and the remaining characters of the password. ¬†The XML to JSON Converter is giving me the error (of course)¬†¬†¬†¬†¬†¬†Error¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†=¬†javax.xml.stream.XMLStreamException:¬†ParseError¬†at¬†[row,col]:[4,26]
    Message:¬†Element¬†type¬†“n”¬†must¬†be¬†followed¬†by¬†either¬†attribute¬†specifications,¬†“>”¬†or¬†“/>”.

    Any idea how to get around this other than requesting a new password and ensuring that it doesn’t contain any < or >?

    Thank you!!!

     

    (0) 

Leave a Reply