Some of the most common activities in SAP CC projects are to handle the calls to external APIs consumed by the platform.

Luckily SAP CC has ready-to-use APIs that could help configuring/using these integrations with API
Registry Module.

The goal of this article is to show you how you could leverage this module to use Standard APIs and avoid developing custom code that could be costly and heavy to maintain.

API Registry Module

The APIRegistry module provides a range of features related to managing event configurations, endpoint configurations, and credentials, as well as exposing events and destinations to a target system.

You can connect SAP Commerce to an external system to allow SAP Commerce to consume the services that the external system provides.

To achieve this connection, you can configure one of the flowing type of credentials:

 

    • Basic Credential

 

    • Consumed Certificate Credential

 

    • Consumed OAuth Credential

 

    • Exposed OAuth Credential

 

Check the link below for more details about this module.

 

 

OAuth 2.0 Authorisation

OAuth 2.0 specification defines 4 types of authorisation flows:

 

    • Authorisation Code

 

    • Resource Owner Password Credentials

 

    • Implicit

 

    • Client Credentials

 

Based on the SAP Commerce APIs (DefaultRestTemplateFactory, DefaultOAuthCredentialsRestTemplateProvider ), Only the flow Client Credentials flow is supported in API Registry Module in SAP CC for Consumed OAuth Credential and Exposed OAuth Credential

It is a critical before to start any implementation of API integrations to double check if the external APIs support OAuth2 client_credentials flow, and if it is the case make sure the partner uses this method to limit the customisation in SAP CC side.

If OAuth2 client_credentials flow is not supported, you can check if you can configure the integration with

 

    • Certificate Credentials

 

    • Basic Credentials

 

 

 If none of these methods is available then you will need to do custom code in SAP CC.

To know more about these flows, you can refer to the following links.

 

 

 

 

 

Configuring APIs in SAP CC

This section will show you how you can configure Consumed APIs using OAuth2 Client Credential flow.

Let’s assume you have an API that use OAuth2 Client Credential flows with Two endpoints.

 

    • 1st endpoint to generate the token.

 

    • 2nd endpoint requesting the resource using the token.

 

Step 1: Authentication (Token
generation)

API

In this API call, scope is used to generate the token, but SAP CC does not allow you to configure it.

 

 

    • BODY:
        • client_id :<CLIENT_ID>
        • client_secret :<CLIENT_SECRET>
        • grant_type : client_credentials
        • scope : <SCOPE> (Optional)

 

Configuration in SAP Commerce Cloud

ConsumedOAuthCredential

See Configuring Credentials

 

Attribute Value
ID <OAUTH2_CREDENTIALS_ID>
OAuth URL https://<AUTHENTICATION_URL_TO_GENERATE_TOKEN>;
Client ID <OAUTH2_CLIENT_ID>
Client Secret <CLIENT_SECRET>

Step 2: API call requiring authentication

API

In this API call, subscription is used in the header, but Standard SAP CC does not allow you to configure it.

 

 

    • Header :
        • Subscription-Key: <SUBSCRIPTION_KEY>
        • Authorization : Bearer <GENERATED_TOKEN>

 

Configuration in SAP Commerce Cloud

Endpoint

See Configuring Endpoints

 

Attribute Value
ID <ENDPOINT_ID>
NAME <ENDPOINT_NAME>
Version <VERSION>
Specification URL https://<GET_OR_POST_REQUEST_REQUIRING_AUTHENTICATION>;

ConsumedDestination

See Configuring Consumed Destinations

 

Attribute Value Documentation
ID <ENDPOINT_ID>
URL Same as Specification URL configured in the Endpoint
Version <VERSION>
Destination Target Destination Target configured to host all
Destination (with Template : false)
Configuring Destination Targets
Endpoint Endpoint configured previously Configuring Endpoints
Credential Credential entry configured previously Configuring Credentials



 

Tests

 

    • Run the server with the Debug Mode

 

    • Test the connectivity with Ping Destination Action

 

    • Error due to the scope missing (scope cannot be configured/initialised in the Standard API) in the first call (Step 1)Expand source
      Caused by: org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException: Access token denied. […] Caused by: org.springframework.security.oauth2.common.exceptions.InvalidRequestException: AADSTS90014: The required field ‘scope’ is missing from the credential. Ensure that you have all the necessary parameters for the login request. Trace ID: 70094699-a056-4d5e-a65f-54014bfc0c00 Correlation ID: 37ad00cb-89f7-4e1f-9f60-2768c515a18d Timestamp: 2023-10-24 13:02:34Z at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:104) ~[spring-security-oauth2-2.5.0.RELEASE.jar:?]

 

    • Forcing the scope in the debugger by adding it to the scope list of the BaseOAuth2ProtectedResourceDetails object (resourceDetails.setScope(List.of(“<SCOPE>”))) solved the issue.

 

    • The first call successfully generated the token using the debugger


    • Then the second call was executed with the token generated by the first call




    • But we had a second error when executing the second call (Step 2) which is due to missing subscription key in the headerExpand source

 

    • ERROR [hybrisHTTP23] [TestDestinationUrlAction] Remote system ([https://<GET_OR_POST_REQUEST_REQUIRING_AUTHENTICATION>]): ping unsuccessful. Returned status code was [401 UNAUTHORIZED]. For more information, see the server log. de.hybris.platform.apiregistryservices.exceptions.DestinationNotFoundException: 401 Access Denied: “{ “statusCode”: 401, “message”: “Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API.” }”

 

    • After forcing the missing subscription in the header, the call was successful.

 

In summary, the Standard API Registry allows you to make a call to protected resource n 2 steps:

 

    • 1st call to generate the token with client Id, client secret and in ‘client_credentials’ mode (OAuth2)

 

    • 2nd call to the target source using previously generated token.

 

If you have any additional parameters such as scope for the OAuth2 call or header parameters, you will need to add them to the Standard API (The next section explain how you can do it).

 

Integrating scope & subscription

This section shows how you can adapt the standard to inject the scope and the header parameters such as subscription.

 

    • Create a CustomDestinationService service that inherits from the OOTB DefaultDestinationService then override testDestinationUrl() method to include support for header subscription and scope
      public class CustomDestinationService extends DefaultDestinationService { private static final Logger LOG = Logger.getLogger(FBDestinationServiceImpl.class); […] final static private String WS_SCOPE=”scope”; final static private String WS_HEADER_SUBSCRIPTION_KEY=”subscription-key”; final static private String WS_HEADER_SUBSCRIPTION_VALUE=”subscription-value”; final static private String CUSTOM_WS_URL_PREFIX=”<CUSTOM_WS_URL>”; @Override public void testDestinationUrl(final AbstractDestinationModel destinationModel) throws DestinationNotFoundException { try { // Setup WS Header final HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.ALL)); headers.setContentType(MediaType.APPLICATION_JSON); // Add subscription’s key and value retrieved from destination.additionalProperties if(destinationModel.getUrl().contains(CUSTOM_WS_URL_PREFIX)) { headers.add(destinationModel.getAdditionalProperties().get(WS_HEADER_SUBSCRIPTION_KEY), destinationModel.getAdditionalProperties().get(WS_HEADER_SUBSCRIPTION_VALUE)); } final HttpEntity<Object> httpEntity = new HttpEntity<>(headers); //Retrieve the Rest template to be executed from Consumer Destination configured on Backoffice final RestTemplate restTemplate = getRestTemplate(destinationModel); // Enrich rest template with the scope retrieved from destination.additionalProperties enrichRestTemplateWithScope(restTemplate,destinationModel); // Call the WS with RestTemplate and return the WS Response final ResponseEntity<String> response = restTemplate.exchange(validateExposedDestinationUrl(destinationModel.getUrl()), HttpMethod.GET, httpEntity, String.class); // Process errors if(response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) { […] } //Here we could process the WS response LOG.info(“WS Response : “+ response.toString()); } // Process errors catch (final HttpClientErrorException | HttpServerErrorException e){[…]} catch (final ResourceAccessException e){[…]} catch (final Exception e){[…]} } private void enrichRestTemplateWithScope(RestTemplate restTemplate, AbstractDestinationModel destinationModel){ //Inject the scope into the oauth2 token call if (restTemplate instanceof OAuth2RestTemplate && ((OAuth2RestTemplate)restTemplate).getResource() instanceof BaseOAuth2ProtectedResourceDetails && destinationModel.getUrl().contains(CUSTOM_WS_URL_PREFIX)){ ((BaseOAuth2ProtectedResourceDetails)((OAuth2RestTemplate)restTemplate).getResource()) .setScope(List.of(destinationModel.getAdditionalProperties().get(WS_SCOPE))); } } }


    • Register the bean in customcore/resources/customcore-spring.xml
      <alias alias=”destinationService” name=”customDestinationService”/> <bean id=”customDestinationService” class=”com.sap.custom.apiregistryservices.services.impl.CustomDestinationService” > <property name=”destinationDao” ref=”destinationDao”/> </bean>

 

Expand source

 

 

    • Configure the consumed destination (Call to the consumed web-service) with scope, subscription-key and subscription-value as entries to ConsumedDestination.additonalProperties Map
      final static private String WEBSERVICES_SCOPE=”scope”; final static private String WEBSERVICES_HEADER_SUBSCRIPTION_KEY=”subscription-key”; final static private String WEBSERVICES_HEADER_SUBSCRIPTION_VALUE=”subscription-value”;

 

In addition to the values configured previously for CustomerDestination

 

Attribute Value
ID <ENDPOINT_ID>
URL Same as specification URL configured in the Endpoint
Version <VERSION>
Destination Target Destination Target configured previously
Endpoint Endpoint configured previously
Credential Credential entry configured previously

You will need to configure ConsumedDestination.additionalProperties as follow.

 

Key Value
scope <SCOPE>
subscription-key <SUBSCRIPTION_KEY>
subscription-value <SUBSCRIPTION_VALUE>

 

    • Now you can test the connectivity with Ping Destination Action highlighted above in the screenshot.

 

    • You can also check the logs on the Console.

 

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x