HCI: Developing custom OAuth 2.0 authentication in iFlows
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.
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.
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.
Step 2
After the call to the token endpoint, the token is provided in an XML response as shown below.
We will then use a Content Modifier step to extract the token value via XPath and store it in a property.
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.
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.
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.
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
Hello,
Basic and O auth login was achieved in Groovy script. And Plain HTTP adapter used at receiver side.
reg, avi
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
Hello Eng,
I have raised one new question on below URL, i am trying to integrate Linkedin using HTTP OAuth2. It will be helpful if you assist on the same.
URL : https://answers.sap.com/questions/719860/linkedin-integration-with-cpi-using-http-oauth2.html
I refered your blog but still have some queries.
Kindly check and revert.
Thanks & Regards,
Karan K
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
Hi Amol
You need to add Concur's Root CA cert to the system.jks keystore in your tenant. Refer following blog:-
HCI: Testing Outbound Connections from HCI
Regards
Eng Swee
Hello Eng Swee,
Great Blog! I am surprised you dont have a fan following on SCN, will happen soon I am sure 🙂
Regards,
Bhavesh
"Fan following" - where do I sign up to get one? 😛
very informative . Thanks for Sharing.
Hi Eng Swee Yeoh,
does anything changed since 2015 in regards to Oauth 2.0 in hci, or still need this iflow development?
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
I´m still waiting for this to be answered as well. Any news on this topic?
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!!!
Just came across this blog post. It's an asset I bookmarked in my library:) Thanks for sharing!
Hello ,
I came accross this blog because of a requirement i am working on. I am trying to connect sharepoint online. However it requires an additional parameter "resource" to be passed in the body while we retrieve access token. Unfortunately i was not able to find an option in the salesforce open connector or the standard odata v2 http adapter to pass the resource parameter. while the 2 step process works i would prefer if we can use the standard tools provided by SAP. any inputs how can an additional parameter be passed in odata v2 adapter (security) ?
Thanks,
Hi Eng Swee,
Nice Blog !!!
Is there any way to have a setup of 'client_credential' grant type in SFDC.
I know my query is related to SFDC system alone, but i saw a Blog that explains CPI integration with SFDC with Client Credentials grant type, let me know if you have any idea or if you have any blogs.
Thanks,
Jovita