Skip to Content
Author's profile photo David Halsband

OAuth 2.0 authentication within a UDF mapping to be included in REST receiver channel

There are different ways how to authenticate within a REST call in Process Integration. Especially when calling a REST API it is more and more common to use OAuth 2.0. Unfortunately the standard REST adapter so far only supports some types of OAuth 2.0 – but not client credentials grant type with an API token.

I have found several great blog posts that have already covered certain aspects of OAuth 2.0 like this one: oAuth Authentication with SAP Process Integration

However I wanted to have both the OAuth 2.0 access token lookup and the actual REST call combined in a single service call. This can be realized with a simple UDF mapping that calls a HTTP receiver lookup channel with method POST and some extra header fields to request the token.The lookup itself was described many times already, like in this post here:
Webservice Calls From a User Defined Function

This token can then be inserted dynamically in the HTTP header of the actual REST receiver channel. The handling of dynamic header fields in the REST adapter is also described here: PI REST Adapter – Define custom http header elements

Access token lookup request: The request for the access token could be defined in different ways. This example shows the grant type client credentials (see RFC6749) and includes a base64 encoded string containing a client id and a client secret. The payload of the message is simply “grant_type=client_credentials”.

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <BASE64_ENCODED_STRING>
grant_type=client_credentials

The expected response of this request will be the access token, here in JSON format.

{
 "scope":"",
 "expired":false,
 "access_token":"<ACCESS_TOKEN>",
 "token_type":"bearer",
 "expires_in":3599
}

REST Call Authorization header: When sending the access token in the “Authorization” request header we have to use the “Bearer” authentication scheme to transmit the access token (as described in RFC6750).

GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer <ACCESSTOKEN>

This is how I have implemented it:

1. Data Types
Define a data type that includes the required parameters for your REST call plus a token field that we will fill in the UDF mapping.

2. Mapping with UDF and parameters
In the graphical message mapping you can do a 1:1 mapping for the REST parameter fields. Then you need to include a UDF to fill the token field.

Unfortunately I was not able to use a channel parameter, because for some reason it does not support HTTP channels. But with two string parameters for “service” and “channel” (and optionally “party) it was possible to hand over the HTTP channel for the token lookup dynamically.
Don’t forget to define and bind the used parameters also in the operation mapping.

	String token = "";
	StringBuffer sb = new StringBuffer();

	// 1. Get a system accessor for a channel. 
	String party = ""; // emtpy values cannot be passed from Directory to the mapping parameters.
	Channel HTTPchannel = LookupService.getChannel(party,service,channel); 
    SystemAccessor accessor = LookupService.getSystemAccessor(HTTPchannel);  
	getTrace().addInfo("Parameters for Token Lookup - Service: "+service+", Channel: "+channel); 
    try{ 
      // 2. Create a payload according to the data type.
      //   Use service.getBinaryPayload() for binary payload,
      //   or service.getTextPayload() for text payloads.
		InputStream inputStream;
		String reqString ="grant_type=client_credentials";
		getTrace().addDebugMessage("Request: "+reqString);
		inputStream = (InputStream) new ByteArrayInputStream(reqString.getBytes());
      com.sap.aii.mapping.lookup.Payload payload = LookupService.getXmlPayload(inputStream); 
      // 3. Execute lookup.
      com.sap.aii.mapping.lookup.Payload result = accessor.call(payload);
     // 4. Parse result, could be better realized with a real JSON parser
		byte[] b = new byte[4096];
		for (int n; (n = result.getContent().read(b)) != -1;){
				sb.append(new String(b,0,n));
		}
	  getTrace().addDebugMessage("Response: "+sb);
		int i =sb.indexOf("\"access_token\":\"")+16;
		int j = sb.indexOf("\"token_type\"");
		token = sb.substring(i,j - 2);
		getTrace().addInfo("Token: "+token);

	}
	catch (Exception e){
		e.printStackTrace();
	}
    finally{
       
        // 5. close the accessor in order to free resources.
        if (accessor!=null) accessor.close();
    }
	return token;

3. HTTP Receiver Channel for OAuth 2.0 Token Lookup
Create a HTTP receiver channel with your target URL and proxy if required. Make sure that you have selected method POST. Also include the Content-Type and Authorization header fields with the base64 value of <CLIENT ID>:<CLIENT SECRET>. This could probably also be done in a more dynamic way, but I wanted to keep things simple here.

4. REST Receiver Channel for API call
Create a REST receiver channel with your target URL and request parameters as required by your call. The channel sets the Authorization header field from the UDF.

5. Integrated Configuration Object with parameters
You also need to fill the mapping parameters here.

Well, this is just one way to implement OAuth 2.0 authentication but I like this one because it is rather simple. I hope this blog is helpful for you.

Assigned Tags

      29 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Vadim Klimov
      Vadim Klimov

      Hello David,

       

      Thank you for informative blog and such illustrative example - very helpful input and handy solution, very much appreciated!

      The only thing I would like to draw your attention to, is limitations of Mapping Lookup API usage. In particular, it works well when communication channel which is looked up and executed (for example by means of SystemAccessor, as it is in the described case), runs on PO's Central Advanced Adapter Engine (cAAE). In case of running the channel on non-central Advanced Adapter Engine (ncAAE), lookup using LookupService and correspondingly SystemAccessor fails. As a result, if mapping with the lookup inside cannot be run on cAAE and has to be executed on ncAAE, feasible workaround is to implement lookup without utilization of Lookup API, but purely by means of generic custom development (which respectively causes increased development efforts and potential increase of maintenance efforts if lookup configuration needs to be changed, and finally technical debt).

      In SAP JavaDocs, there is very light hint on this - for example, in SystemAccessor, it is stated: "Accessor for a communication with a remote system. The communication is established via adapters of the Integration Server.", which may be interpreted as only communication channels running on cAAE (of Integration Server) make sense here.

      The last time I faced this limitation, was when assessing options for lookup implementation in PO release 7.4, but I assume the same restriction may remain in release 7.5.

       

      Regards,

      Vadim

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      Thanks Vadim for pointing this out. I didn't know about this limitation.

      Best regards,
      David

       

      Author's profile photo Julio Cesar Torelli
      Julio Cesar Torelli

      Excelent blog 🙂  Very helpful

      Author's profile photo Rupa Bodepudi
      Rupa Bodepudi

      Can I give &authToken=24873274323... value at the URL - GET??

      Please let me know. Thank you

       

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      Not sure. I guess you have to try it out.

      Author's profile photo Rupa Bodepudi
      Rupa Bodepudi

      My scenario is using REST Services; using GET can we pass on &authToken in the URL?

      Please clarify on this. Thank you.

       

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      Sorry, no idea. Please try it out.

      Author's profile photo Ravi Gupta
      Ravi Gupta

      Hi David,

      I have to use NTLM authentication having user id + password + domain name. I have tried different combination of base 54 encoded strings of these 3 values as mentioned below:

       

      Authorization NTLM base64 encoded string

      But I am still getting authorization issue. Can you suggest how your approach works in this case.

       

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      I have never used NTLM, so I cannot give you any hints here.

      Author's profile photo Former Member
      Former Member

      Hi David,

      thanks for your great Blog. I have one question: i followed all the steps, i become this Error. "XI AF API call failed. Exception: 'XMLPayload is empty'. The HTTP Response Code is 200. That means in my opinion, that the HTTP-Lookup channel could not retrieve the Json payload.

      I would like to add, that i have the same authentication scenario as yours.

      Thanks for giving me some advices to proceed my scenario.

       

       

       

       

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      So does it mean that the API token call works? Can you see the token in the log?

      Author's profile photo Navneet Sumit
      Navneet Sumit

      Hi David,

      Excellent Blog.

      I am trying to implement the same, but getting 401 unauthorised error. When I connected with  my rest service provider, they said that client ID/Secret is not in base64 encoding.

      How you are generating base64 encoded Client ID/Secret? I used some online tool to convert my string to base64 code but it did not worked.

       

      Thanks,

      Navneet

       

       

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      Hi Navneet,

      I am using an online base64 encoding tool.

      1. Encode 'apitoken:<APITOKEN>, e.g. apitoken:8CUjyOBmSOXkBWS7cAsjuaeKnZT5YG3VQKra7kSy
      2. Copy result, e.g.
        YXBpdG9rZW46OENVanlPQm1TT1hrQldTN2NBc2p1YWVLblpUNVlHM1ZRS3JhN2tTeQ==
      3. Go to your HTTP API Lookup channel, open tab Advanced, add Authorization header field, Insert value 'Basic' + base64string (from step 2), e.g.
        Basic YXBpdG9rZW46OENVanlPQm1TT1hrQldTN2NBc2p1YWVLblpUNVlHM1ZRS3JhN2tTeQ==

       

      Author's profile photo Former Member
      Former Member

      David Halsband,

       

      Thanks for sharing the document, however I have a doubt here...

      As you have added TOKEN field in target structure which might not be accepted by target server...

      Is there any other way to assign access_token from UDF directly to HTTP Header value in REST Receiver adapter?

       

       

      BR,

      Rashmi

      Author's profile photo David Halsband
      David Halsband
      Blog Post Author

      Hi Rashmi,

      you can write the TOKEN value after the mapping-lookup into the ASMA fields, which you can use as a configurable parameter. Then you don't need a TOKEN field in your target structure.

      Best regards,
      David

       

      Author's profile photo Former Member
      Former Member

      Hello David,

      I have tried the dynamic configuration using the below code :

      var1 = token;
      getTrace().addInfo("var1: "+var1);

      DynamicConfiguration dynConf = (DynamicConfiguration)all.get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);
      DynamicConfigurationKey key = DynamicConfigurationKey.create( "http://sap.com/xi/XI/System/REST","Authorization");
      dynConf.put(key, var1);

      Which is not working in my case.

      Could you please let me know how to achieve

      Author's profile photo Ankit Mishra
      Ankit Mishra

      I used the below code & it's working for me.

      DynamicConfiguration dynamicConfig = (DynamicConfiguration) container.getTransformationParameters().get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);
      DynamicConfigurationKey key = DynamicConfigurationKey.create("http://sap.com/xi/XI/System/REST","Authorization");
      dynamicConfig.put(key,token); //token is the parameter containing the value
      return "";

      Author's profile photo Former Member
      Former Member

      Hi David,

      PI 7.31 SP14 rest adapter does not have HTTP headers tab.

      Is there any other method using which we can send Authorization header value in the receiver rest adapter.

      Thanks,

      Vinay

      Author's profile photo Former Member
      Former Member

      Hi,

      I have tried in the same way, however,  the UDF is not looking up with the http channel that was configured.   I have created the necessary service and channel parameters in Message Mapping and Binding in Operation Mapping (Parameter of type string as HTTP adpater do not have Adapter parameter type). Also, configured the Parameters in Integration directory ICO. Could you please help on why the lookup is not happening.

      Thanks,

      Prem

       

       

      Author's profile photo Former Member
      Former Member

      Hi David Halsband,

       

      Thank you for writing this blog.

       

      I have a similar kind of requirement of authorization using OAuth2.0 Grant Type flow. Your blog has helped me in implementing the OAuth2.0 Authorization.

      But I am not able to get the desired output.

       

      First of all, I would like to mention that I am using REST Receiver adapter instead of HTTP receiver Adapter. Having said that I have done other configuration as stated in the blog.

      when I am trying to fetch the access token from message mapping(from Test Tab), I am getting following error in channel :
      Server returned code: 504
      HTTP error occurred: Gateway Time-out

      This didn't lead me anywhere. So, In order to understand the error, I re-run the scenario and started the XPI_INSPECTOR to fetch more logs.

      From XPI_INSPECTOR logs, I could see following error : com.sap.aii.utilxi.hmi.api.HmiMethodFault: Exception during processing the payload. Error when calling an adapter by using the communication channel

      • Is the RAW Text input to the "accessor.call(payload)" method is the reason for the error found in XPI_INSPECTOR?
      • As I am using REST adapter, I need to select Request data format that could be either XML or JSON. But we are sending RAW text as body. how should I tackle this?

      Any help would be appreciated.

       

      Thank you.

      Regards,
      Ajito

      Author's profile photo Ankit Mishra
      Ankit Mishra

      Hi David,

       

      Nice Blog.

       

      Just wanted to know if there's any other way to achieve this. I my case there will be 30-50K messages per day. Each message will make a Lookup call which will surely slow down the system. We are still searching for other possible options to fulfill this task. Let me know if you have anything  else in your mind for this.

      Author's profile photo Otto Frost
      Otto Frost

       

      Dear David and Ankit,

       

      I have the exact same requirement.

       

      Did you find another solution?

      We don't want to request a new token for every call. A new token should only be requsted once the previous one is about to expire.

      The problem is, where do you best store the token in PO? As PO is stateless there is no Application or Session object where it could be stored. Maybe a JMS queue is the best option? Much faster and easier to implement than a database table? Plus expiry time on the message.

       

      You don't want to use lookup in the mapping becasue if the server you connect to is unavailable the whole PO may be blocked because all mapping threads may be used up.

      Regards

      O

      Author's profile photo mohammed aathif
      mohammed aathif

      Hi David Halsband

       

      Thanks for such an informative Blog.

      While trying to get access token from the authentication server, We encountered with an error in HTTP_AAE adapter.

      STATUS_CODE_NOT_OK- 502 Proxy Error ( The specified network name is no longer available.)

      Below is my Communication Channel Configuration with Proxy.

       

      We are on SAP PI 7.31 Single stack SP22.

      I was able to get access token using POSTMAN with the same configuration, Please help me what is causing such error in HTTP_AAE adapter.

      Thanks,

      Aathif

       

      Author's profile photo Manjunath Bannur
      Manjunath Bannur

       

      Dear David,

      we got one business requirement where we need to call the JSON API in our ESS portal.

      we need to call the API using the authorization token generated run time dynamically.

      Process : - first we have to pass one token id and then get the One more access token.

      Based on the above second  access token call the below API.

      ADD API  https://apigee.net/v1/members

      Update a member API end point in QA: https://apigee.net/v1/members/{memberId}

      I am new to This topic.

      so kindly can u help me asap  with the complete solution step by step.

      Regards,

      Manjunath

      Author's profile photo marcos mendes
      marcos mendes

      Hi David Halsband

      Your post is very good and important I appreciated it !!

      iI'm having problems after i have the TOKEN in my case are(SessionID and UserAuth).

      How can i configured the HTTP_AAE for send this fields(SessionID/UserAuth) inside cookie in the HEADER message?

      All configuration that i have done has the same problem "STATUS_CODE_NOT_OK-Unauthorized".

       

      If you have any ideas i'll be greatful!!!

       

       

      Author's profile photo Rajesh PS
      Rajesh PS

      Hello David Halsband

       

      Please suggest. Thanks!

       

      https://answers.sap.com/questions/12882619/how-to-store-and-retrieve-oauth-tokens-in-sap-po.html

      Author's profile photo Philippe Addor
      Philippe Addor

      A similar and very promising approach but including caching functionality has been described here by Martin Buselmeier: https://blogs.sap.com/2017/07/05/api-token-via-http-lookup-in-adapter-module/

       

      Author's profile photo Shaik Khaja Karimulla
      Shaik Khaja Karimulla

      hello David,

       

      I am also facing Look Up Error for HTTP Adapter name, where this adapter type not coming in the parameters, I have to select only Simple type and after getting error as  "channel object id not be found in integration server java cache".

       

      Regards,

      Karimulla.

      Author's profile photo Umberto Orazzo
      Umberto Orazzo

      Hi David

       

      but in the parameters "service" what should be put.and in the configuration of the http_aae channel host port and path how they should be configured

       

      Thanks

      Umberto