Motivation
Previously, communication from cloud to on-premise via a technical communication user was only possible via basic authentication. Here, I try to cover a new, more secure means to achieve this using
OAuth and x.509 certificates.
. That way, I do not have to share any credentials of the on-premise system with the cloud environment. Additionally I can use either secret based or mTLS based authentication mechanisms to obtain the access token, where the latter is generally considered more secure than basic authentication. We cover the mTLS based authentication mechanism in this blog.
Learn more here (
Authenticating Users against On-Premise Systems | SAP Help Portal).
The following diagram depicts the flow from SAP
S/4HANA Cloud, Public Edition.
For general information on what OAuth with mTLS means, refer https://oauth.net/2/mtls/ and https://datatracker.ietf.org/doc/html/rfc8705 . If you are unaware of how mTLS works, it is recommended to read through the above links before continuing.
Prerequisites
- (see here)
- On Premise system configured to trust certificates forwarded by the SAP Cloud Connector (see here)
- Subaccount with configured SAP Cloud Connector (see here)
- SAP BTP ABAP Environment or SAP S/4HANA Cloud system connected to the subaccount where the cloud connector is configured (see here)
- For OAuth with mTLS: A valid certificate to the ABAP system on SAP BTP ABAP Environment
- A SOAP/OData service exists in the on-premise system that is ready for consumption
Scenario
Here, I consume an
OData service and a
SOAP service from an on-premise ABAP system via technical user propagation using OAuth with mTLS (Certificate based authentication). I
For
OData, we choose the standard product
master service available with "/sap/opu/odata/SAP/API_PRODUCT_SRV/". For
SOAP, we use a custom SQRT service. However, both of these are just examples and the process should be the same for any other
OData/
SOAP services.
All steps of the blog would be applicable for both SAP
S/4HANA cloud public edition and
SAP BTP ABAP Environment unless stated otherwise.
Consume services from the on-premise system
Development - Code based consumption
Persona: Developer in the ABAP system hosted on
SAP BTP ABAP Environment or Developer in SAP
S/4HANA Public Cloud system (using SAP S/4HANA Cloud ABAP Environment aka
Embedded Steampunk)
First set up the code to read data from an on-premise system. Here, I consume the standard product
OData service and a custom
SOAP service that calculates square root. This blog focuses on the security aspects when consuming services from on-premise systems.
Consumption of OData service
Create a service consumption model to consume the standard product OData service.
Create a new service consumption model object
Choose the Remote Consumption Mode as
OData and provide a suitable name, description
Upload the service metadata of the standard product API
Follow through the wizard to have the required objects generated
Create outbound service for Product OData service
The path to the standard Product
OData service can be found from the SEGW transaction or the SAP Gateway client in the on-premise system.
On the ABAP system on the
SAP BTP ABAP Environment or SAP
S/4HANA Cloud, Create an outbound service in
ADT with type HTTP and provide the default path prefix as identified above.
Create communication scenario for the Product OData service
Now, we need to expose the service for communication. Here, we create a communication scenario with
ADT
Under the outbound tab, add the outbound service created above, ensure and publish the communication scenario
Create a class to read product details from the standard OData service
CLASS zcl_product DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ZCL_PRODUCT IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
lt_business_data TYPE TABLE OF za_product,
lo_http_client TYPE REF TO if_web_http_client,
lo_client_proxy TYPE REF TO /iwbep/if_cp_client_proxy,
lo_request TYPE REF TO /iwbep/if_cp_request_read_list,
lo_response TYPE REF TO /iwbep/if_cp_response_read_lst.
TRY.
" Create http client
DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_COM_PROD'
comm_system_id = 'S4H_1909_CAL'
service_id = 'Z_PRODUCT_ROOT_REST' ).
lo_http_client = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
ASSERT lo_http_client IS BOUND.
" If you like to use IF_HTTP_CLIENT you must use the following factory: /IWBEP/CL_CP_CLIENT_PROXY_FACT
lo_client_proxy = cl_web_odata_client_factory=>create_v2_remote_proxy(
EXPORTING
iv_service_definition_name = 'Z_SCM_PRODUCT'
io_http_client = lo_http_client
iv_relative_service_root = '' ).
" Navigate to the resource and create a request for the read operation
lo_request = lo_client_proxy->create_resource_for_entity_set( 'A_PRODUCT' )->create_request_for_read( ).
lo_request->set_top( 50 )->set_skip( 0 ).
" Execute the request and retrieve the business data
lo_response = lo_request->execute( ).
lo_response->get_business_data( IMPORTING et_business_data = lt_business_data ).
out->write( lt_business_data ).
CATCH /iwbep/cx_cp_remote INTO DATA(lx_remote).
" Handle remote Exception
" It contains details about the problems of your http(s) connection
out->write( lx_remote->get_longtext( ) ).
CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).
" Handle Exception
out->write( lx_gateway->get_longtext( ) ).
CATCH cx_web_http_client_error INTO DATA(lx_web_http_client_error).
" Handle Exception
out->write( lx_web_http_client_error->get_longtext( ) ).
CATCH cx_http_dest_provider_error INTO DATA(lx_destination).
"handle exception
out->write( lx_destination->get_longtext( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.
Consumption of SOAP service
Create a service consumption model to consume a custom SQRT SOAP service
Choose the Remote Consumption Mode as Web Service and provide a suitable name/description
Upload the
WSDL metadata of the
SOAP service
Follow through the wizard to have the required objects generated
Create outbound service for SQRT SOAP service
Identify the request URI for the
SOAP service from the SOAMANAGER transaction on the on-premise system
Identify the service interface. This is the proxy class in the service consumption model created for the
SOAP service
Create an outbound service using
ADT with type
SOAP in the ABAP system on the
SAP BTP ABAP Environment or SAP
S/4HANA Cloud, public edition
Create communication scenario for SQRT SOAP service
Now, we need to expose the service for communication. Here, we create a communication scenario with
ADT
Under the outbound tab, add the outbound service created above, ensure for authentication with grant type "Client Credentials" and publish the communication scenario
Create a class to read the square root of an integer from a custom SOAP service
CLASS zcl_soap_sqrt DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ZCL_SOAP_SQRT IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
try.
data(destination) = cl_soap_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_COM_SQRT_SOAP'
).
data(proxy) = new zco_z_sqrt_srv(
destination = destination
).
data(request) = value zsqrt_fm( input_num = 4 ).
proxy->zsqrt_fm(
exporting
input = request
importing
output = data(response)
).
out->write( response-result ).
"handle response
catch cx_soap_destination_error INTO DATA(destination_ex).
"handle error
out->write( destination_ex->get_longtext( ) ).
catch cx_ai_system_fault INTO DATA(system_ex).
"handle error
out->write( system_ex->get_longtext( ) ).
catch zcx_zsqrt_fm_exception INTO DATA(fm_ex).
"handle error
out->write( fm_ex->get_longtext( ) ).
endtry.
ENDMETHOD.
ENDCLASS.
Create OAuth Client for Outbound Communication User
Persona: Sub account administrator or space developer on
SAP Business Technology Platform
I first need to obtain an OAuth client that is provided by an authorization server. This is trusted by the
SAP Cloud Connector (trust established by connecting the
Cloud Connector to a sub account).
One way to do so is by creating an instance of the Authorization and Trust Management service in the sub account connected to the
Cloud Connector, which results in an OAuth client, which can be used to obtain access tokens. This is done in the sub account connected to the
SAP BTP ABAP Environment system or to SAP
S/4HANA Cloud
- Go to the Marketplace in the Business Technology Platform Cockpit of the sub account
Search for the Authorization and Trust Management Service
- Create an instance of service plan application of the Authorization and Trust Management Service (no additional settings are required beyond the first wizard page)
Persona: Subaccount administrator or space developer on
SAP BTP, administrator on the ABAP system on
SAP BTP ABAP Environment/SAP
S/4HANA Cloud
To be able to use a client certificate for the OAuth flow, we need to map the certificate of the ABAP system to the OAuth Client (XSUAA instance) we just created.
- As an administrator, logon to the Fiori Launchpad of the ABAP system on SAP BTP ABAP Environment or to the Fiori Launchpad of SAP S/4HANA Cloud. Find the app Maintain Client Certificates.
- Download the public key in base64 format from the ABAP system
NOTE: On the SAP BTP ABAP Environment, the default client certificate cannot be used for mTLS since this is self-signed at the time of writing this and a valid, signed certificate is needed. On SAP S/4HANA Cloud, it is recommended to use the default client
- Put the public key in the generated file "public.pem" into the following json replacing "<public key>". Ensure to adjust the new line characters to resolve errors in the JSON
{
"credential-type": "x509",
"x509": {
"certificate": "<public key>",
"certificate-pinning": false
}
}
- Keep the resulting service key for later reference
Create a Communication System
Persona: Administrator of the ABAP system hosted on
SAP BTP ABAP Environment or Administrator in the SAP
S/4HANA Public Cloud system
The Communication System is used to pass the OAuth client details to the ABAP system and to configure how and where the access token should be transported.
- Create a new Communication System using the Communication System app.
- Configure the on premise system (based on hostname/port in the cloud connector configuration) and enable the cloud connector, providing the location ID.
- Token Endpoint: <URL from service key>/oauth/token
- mTLS Endpoint: <certurl from service key>/oauth/token
- In the "Users for Outbound Communication" section create a new outbound user with authentication method oAuth 2.0
- Save the Communication System
Create a Communication Arrangement
Persona: Administrator of the ABAP system hosted on
SAP BTP ABAP Environment or Administrator in the SAP
S/4HANA Public Cloud system
- Create a Communication Arrangement for the desired Scenario and use the Communication System we just created
- Make sure that the Authentication Method is OAuth 2.0 and the Client ID is the one we just configured
Configure the Subject Pattern in Cloud Connector
Persona: Cloud connector administrator
Documentation:
Configure Subject Patterns for Principal Propagation | SAP Help Portal
To define how the OAuth access token shall be handled in the
Cloud Connector and how a resulting client certificate should look like, we define a pattern, which is applied to the incoming access token.
In our example we take the client id and put it into the CN of the subject name.
- Open the Configuration > OnPremise menu in the Cloud Connector
- Scroll down to the Principal Propagation section
- Create a new Subject Pattern
- Enter a Condition that the ${user_type} is Technical
- Enter the CN as ${client_id} to reference the client id of an incoming access token
- Provide appropriate values for the other fields
- Generate a sample certificate
- Enter the
Map Certificate to an On-Premise Technical User
Persona: Administrator
To be able to authenticate a user with the certificate issued by the
Cloud Connector, we map the Subject (OAuth Client ID) and Issuer (
Cloud Connector) combination to an actual user in the on premise system.
- Open transaction certrule in the on premise system
- Go into edit mode
- Upload the sample certificate
- Save and verify the new mapping is created
End to End Test and Validation of technical user propagation
Persona: Developer, Cloud connector administrator, Subaccount administrator/space developer
Run the classes implementing IF_OO_ADT_CLASSRUN that we defined above for the
SOAP and
OData services in
ADT. Notice the output in the console.
To verify this call indeed happened with the technical user, check the most recent requests from cloud to on-premise in the cloud connector under the "Monitor" menu item. Notice the "User" column reflects the client ID
To verify at the on-premise system, use transaction STAD and filter with the technical user configured during the explicit mapping
Notice the calls indeed happened with this technical user