Skip to Content
Author's profile photo Former Member

oAuth Authentication with SAP Process Integration

Swisscom, the leading ICT House in Switzerland provides under api-developer.swisscom.com a central API platform with a hugh number of different APIs. For example, it is possible to sent SMS messages or realize SMS Token authentication.

Within a project I had to realize an interface to one oft he provided APIs. Before a single API can be used it is necessary to call the authentication API (based on oAuth2) to receive an access token. The received token has to included into the http header when you call the real API. The authentication process has been realized based on the oAuth 2.0 standard. This standard is used on many API platforms and cloud services so it can also be interested for you.

The biggest problems have been:

  • To oAuth Service has to be called by a post. And the http body did not include a XML message.
  • As I had some problem to overwrite some http header values in the REST Adapter I had to use the HTTP Adapter why I had to write my own JSON to XML conversion.

Calling the oAUTH Service

If you want to authenticate against the oAUTH service it is necessary to send a client id and a secret key. Client id and secret key have to be combined.

            <client id>:<secret key>

And after that create combination has to become 64 Bit encoded. That 64 Bit encoded string has to send within the http header to the authentication service.

I decided to define on sender side the following structure.

oAuth_struktur.png

The idea behind was that the sender has the flexibility to send directly the base64 encode string (base64) or client id and secret key ()client_id, client_secret) so that the mapping encodes the values. Furthermore he has the possibility to take influence on the authentication service with the grant_type element. The grant_type by default is client_credentials. In that case a new access token ist created. But if the grant type is set to refresh_token an existing token is renewed.

As the http request had to be posted and the http body hat too look like

grant_type=client_credentials / grant_type=refresh_token

I used a java mapping wishing the interface. The used Code is:


package com.my.mapping;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.DynamicConfiguration;
import com.sap.aii.mapping.api.DynamicConfigurationKey;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import com.sap.aii.utilxi.core.io.IOUtil;
import org.apache.commons.codec.binary.Base64;

If the request is sending id and key it has to be base64 encoded. For that I integrated a library from Apache. That can be downloaded here.


public class CreateHTTPBody extends AbstractTransformation {
            public void transform(TransformationInput input, TransformationOutput output)
                                   throws StreamTransformationException {
                       try {
                                   AbstractTrace absTraceLog = this.getTrace();
                                   absTraceLog.addDebugMessage("\n Start Java Mapping-");
                                   DocumentBuilder domFactory = DocumentBuilderFactory.newInstance()
                                                          .newDocumentBuilder();
                                                              
                                   Document doc = domFactory.parse(input.getInputPayload()
                                                          .getInputStream());
                                   doc.getDocumentElement().normalize();
               
                                   String authCode = "";
                           
                                   //User / Passwort vorhanden?
                                   if (doc.getDocumentElement().getElementsByTagName("client_id")
                                                          .getLength() > 0 &&
                                               doc.getDocumentElement().getElementsByTagName("client_secret")
                                                          .getLength() > 0
                                                          &&
                                                          doc.getDocumentElement().getElementsByTagName(
                                                          "client_id").item(0).getTextContent() != ""
                                                                      &&
                                                                      doc.getDocumentElement().getElementsByTagName(
                                                                      "client_secret").item(0).getTextContent() != ""
                                                          )
                                       
                                   {
                                       
                                               String toEncode = doc.getDocumentElement().getElementsByTagName(
                                               "client_id").item(0).getTextContent() + ":" + doc.getDocumentElement().getElementsByTagName(
                                               "client_secret").item(0).getTextContent();
                                       
                                               authCode = new String(Base64.encodeBase64(toEncode.getBytes()));
                                               absTraceLog.addDebugMessage("\n Auth Code: " + authCode);
                                       
                                   }
                           
                                   if (doc.getDocumentElement().getElementsByTagName("base64")
                                                          .getLength() > 0
                                                          &&
                                                          doc.getDocumentElement().getElementsByTagName(
                                                          "base64").item(0).getTextContent() != ""
                                   ) {
                                       
                                               absTraceLog.addDebugMessage("\n base64 Anzahl: "
                                                                      + doc.getDocumentElement().getElementsByTagName("base64")
                                                                                             .getLength());
                                                                                     
                                               absTraceLog.addDebugMessage("\n base64 Value : "
                                                                      + doc.getDocumentElement().getElementsByTagName(
                                                                                             "base64").item(0).getTextContent());
                           
                                               authCode = doc.getDocumentElement().getElementsByTagName("base64").item(0).getTextContent();
                                               absTraceLog.addDebugMessage("\nBase 64:" + authCode); 
                           
                                   }

For the authentication it is necessary that later in the http header the base64 encoded authentication string is send. For that I used the folliwng

solution. The string is written into the dynamic configuration of the message.


                                   // Create Dynamic Configuration Object
                                    DynamicConfiguration conf = input.getDynamicConfiguration();
                                   DynamicConfigurationKey keyHeader1 = DynamicConfigurationKey.create( "http://sap.com/xi/XI/System", "HeaderFieldOne");
                                   conf.put(keyHeader1, "Basic " + authCode);                           

The information is written into the HeaderFieldOne field. So that it can be accessed later by the adapter.


absTraceLog.addDebugMessage("\n-------------------------------------");
                                  String strFlatData = "";
                                   InputStream inputStream = input.getInputPayload().getInputStream();
                                   inputStream.close();
                                   strFlatData = IOUtil.copyToString(inputStream, "UTF-8");
                                   strFlatData = strFlatData.replaceAll("&amp;", "&");
                                   if (doc.getDocumentElement().getElementsByTagName(
                                                                                             "base64").item(0).getTextContent() != "") {
                                         
                                               strFlatData = "grant_type=" + doc.getDocumentElement().getElementsByTagName(
                                               "grant_type").item(0).getTextContent();
                                         
                                   } else {
                                               strFlatData = "grant_type=client_credentials";
                                   }
                                   // output.getOutputPayload().getOutputStream().write(strFlatData.substring(strFlatData.indexOf("?>")+2).getBytes());
                                   output.getOutputPayload().getOutputStream().write(
                                                          strFlatData.getBytes());
                       } catch (Exception ie) {
                       }
            }




In case you want to use the mapping also you don’t forget to upload the Apache Base64 Library as imported archive into your ESR.

IA JAR.png

At first I wanted to use the new REST Adapter. But as I had some issues by manipulating the http header. I decided to use the http Adapter. On the created communication channel it following settings has been necessary.

At first I had to set the content-type within the http header.

/wp-content/uploads/2016/03/httpheader_903552.png

Furthermore the authentication string that has been put into the dynamic configuration has to be overtaken into the http Adapter. For that you have to activate the Adapter-Specific Message Properties. There check HTTP Header Fields and configure the HeaderFieldOne to Authorization.

header field.png

Later during the runtime the Adapter will take the information from HeaderFieldOne and send it with the http header as

Authorization:base64encodedString


Processing the oAUTH Service response


The oAuth service sends his response back in JSON. As I used the http Adpater it was necessary that I transform the JSON result to XML. For that I created a java mapping that is running before my origin message mapping.



package com.my.mapping;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import org.json.JSONObject;

For an easy and fast transformation from JSON to XML I used an additional JAR library that can be downloaded here.


The additional JAR library has to imported also as Imported Archive into the ESR.

json jar.png

In the Operation Mapping I configured two Mappings. The first is the JAVA mapping that transforms the JSON and after that the Message Mapping is used.

/wp-content/uploads/2016/03/om_903896.png


When the interface runs the son response is transformed into XML before the origin mapping runs.

JSON Response Transformed XML
{
   “access_token”: “myToken”,
   “scope”: “read-mobiletypes”,
   “token_type”: “bearer”,
   “expires_in”: “2591999”
}
<?xml version=“1.0” encoding=“UTF-8”?>
<ns0:scsApiTokenResponse xmlns:ns0=http://pi.com/pi1/Standard>
  
<access_token>myToken</access_token>
  
<scope>read-mobiletypes</scope>
  
<token_type>bearer</token_type>
  
<expires_in>2591999</expires_in>
</ns0:scsApiTokenResponse>


Now the client (SAP PI) is identified at the platform. Later in the process the sender can use the token to authenticate against the API platform.

Assigned Tags

      10 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo David Halsband
      David Halsband

      Hi Stefan,

      thanks for your informative blog. I have tested your implementation and it helped me to understand oAuth2. For my use case I have created a UDF with a channel lookup to request the token for the REST call. It is described in my blog OAuth 2.0 authentication within a UDF mapping to be included in REST receiver channel

      Best regards,
      David

      Author's profile photo Stefan Zimara
      Stefan Zimara

      Hi David,

      I read your blog. Really a nice approach.

      cheers,
      Stefan

      Author's profile photo Former Member
      Former Member

      Hi Stefan,

      I have a requirement to connect with MS Dynamics CRM system and using oAuth Authentication to  connect with CRM system. I followed your blog, but in the sender side I'm facing an issue in Message Mapping with error message "Check Result for Object oAuthMapping Source text of object Message Mapping: oAuthMapping | http://abc.com/xyz has syntax errors:​"

      Imported all the JARs into Imported Archives, but couldn't find a way out. Please suggest any solution

      Author's profile photo Stefan Zimara
      Stefan Zimara

      Hi Amal,

      as the blog entry has been written by an old SAP service user (I don't have access to the assigned email) I did not get a notification about blog comments.

      In a former project I set up a connection to Dynamics. There I was able to use simply basic authentication.

      Some week ago I had to setup an interface to a Navision solution. I suppose that in the meantime that is the same like Dynamics. There we had to use NTML

      Maybe that there is a possibility to use not oAuth authentication.

      Regarding to you error. I can imagine that the oAuthentication on your dynamics system has a different behavior from the platform I had to authenticate to.

      Can you see the message in your message monitoring?

      cheers,
      Stefan

       

       

       

       

      Author's profile photo Former Member
      Former Member

      Hi Stefan,

      Thanks for the reply. The issue was because of a jar file missing in Import Instructions. Once I added the required files, the error got resolved.

      However I'm not able to configure connection to Dynamics system yet. Is it possible to connect with Dynamics 365 Online with basic authentication ? I tried it out, can see the messages in Message Monitoring, but it will be in Waiting state and will be in System Error state eventually.

      Any idea how to achieve this connection setup with Dynamics system ?

      Author's profile photo Stefan Zimara
      Stefan Zimara

      Hi Amal,

      do you see something in the communication channel monitoring? How does the process log of your message look like? Any information in there that can be helpful?

      In addition. I always try at first to connect via SOAP UI (https://www.soapui.org) to the SOAP or Rest Service. If that works I am sure that the service is working and I get additional information in http log etc.

      Do you have contact to person that is responsible for the Dynamics service? Ask the person about the different authentication possibilities. Maybe there is one that is easier than oAuth.

      cheers,
      Stefan

      Author's profile photo Former Member
      Former Member

      Hi Stefan,

       

      The process log in communication channel gives an error message, 'An error occurred when verifying security for the message.'. I get the same error in SOAP UI tool also. We tried with Basic authentication in SOAP UI.

      While discussing with Dynamics team, they informed us about oAuth, but we have issue in achieving this.

      Is it possible to set up connection by using just Basic authentication method ? Or is it necessary to go with oAuth?

      Author's profile photo Varun K
      Varun K

      Hi Team,

       

      I have exact requirement as mentioned in the blog.  I am using PO 7.5. Can it be achieved using REST Adapter instead using Java Mapping/HTTP Adapter ?

       

      Thanks,

      Varun

       

       

      Author's profile photo Stefan Zimara
      Stefan Zimara

      Hi Varun,

      in the meantime there should be the possibility to use the REST Adapter.

      https://blogs.sap.com/2019/04/25/fetch-oauth-token-in-rest-is-now-out-of-the-box/

      cheers,
      Stefan

      Author's profile photo Jignesh Panachand Shah
      Jignesh Panachand Shah

      Hi ALL,

      I have the same issue

      response code: 400 response: {"error":"invalid_client","error_description":"Client id or client secret was not provided"}

      Kindly let me know how to resolve this.

      Thanks,

      Jignesh