CRM and CX Blogs by SAP
Stay up-to-date on the latest developments and product news about intelligent customer experience and CRM technologies through blog posts from SAP experts.
cancel
Showing results for 
Search instead for 
Did you mean: 
mohamedvallbah
Advisor
Advisor

Introduction


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

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

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.

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.

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.








Conclusion


Now you will be able to identify if the consumed web-services are using an authentication compatible with API Registry Module in SAP Commerce Cloud and leverage this feature to track all the APIs and reduce customisation efforts.