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 grant_type=client_credentials
The expected response of this request will be the access token, here in JSON format.
{ "scope":"", "expired":false, "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
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 :. 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.
New NetWeaver Information at SAP.com
Very Helpfull