I am using the SAP Cloud SDK for Java to do CRUD on the SalesOrder APIs in S/4. Everything works well in that I can carry out these actions from Postman. However, these requests from Postman only work if I include a pre-request script to get a csrf token as outlined in this blog post
If I run the requests without the pre-request script outlined in the blog post, I get a '403 Forbidden'. As I said it works from Postman, but I would like to understand how this should be handled without the need for this script, for example if I was making a request from another application. Does the SDK allow me to handle this from the application code somehow. Maybe I am missing something.
Thanks for your time.
EDIT:
I am not making requests to the S/4 directly from Postman. I have an app deployed which is using the Cloud SDK to make the requests to S/4. It works if I use the pre-request script to fetch the CSFR token and attach it to the request before I send it, but 403 if I don't. So, if we imagine I am not using Postman but some ui somewhere to fill a form and send this request my understanding is that I shouldn't, as you suggested, have to worry about this token, that my service in the middle which uses the SDK and the VDM should handle this for me. This is what I am struggling to understand.
This is the servlet code:
@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
String body = IOUtils.toString(request.getReader());
JSONObject so = new JSONObject(body);
String distributionChannel = so.get("DistributionChannel").toString();
String salesOrderType = so.get("SalesOrderType").toString();
String salesOrganization = so.get("SalesOrganization").toString();
String soldToParty = so.get("SoldToParty").toString();
String organizationDivision = so.get("OrganizationDivision").toString();
String material = so.get("Material").toString();
String requestedQuantityUnit = so.get("RequestedQuantityUnit").toString();
SalesOrderItem salesOrderItem = SalesOrderItem.builder()
.material(material)
.requestedQuantityUnit(requestedQuantityUnit).build();
SalesOrder salesOrder = SalesOrder.builder()
.salesOrderType(salesOrderType)
.distributionChannel(distributionChannel)
.salesOrganization(salesOrganization)
.soldToParty(soldToParty)
.organizationDivision(organizationDivision)
.item(salesOrderItem)
.build();
try {
final ErpHttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).asHttp()
.decorate(DefaultErpHttpDestination::new);
final SalesOrder storedSalesOrder = new CreateSalesOrderCommand(destination, new DefaultSalesOrderService(),
salesOrder).execute();
response.setStatus(HttpServletResponse.SC_CREATED);
response.setContentType("application/json");
response.getWriter().write(new Gson().toJson(storedSalesOrder));
logger.info("Succeeded to CREATE {} sales order", storedSalesOrder);
} catch (final Exception e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
logger.error(e.getMessage(), e);
logger.error("Failed to CREATE sales order", e);
}
}
And the CreateSalesOrder command:
public SalesOrder execute() {
return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig);
}
protected SalesOrder run() {
try {
return salesOrderService.createSalesOrder(salesOrder).execute(destination);
} catch (final ODataException e) {
throw new ResilienceRuntimeException(e);
}
}
I am using the version 3.16.1 of the SDK and have set logging level to DEBUG for the SDK in the manifest:
SET_LOGGING_LEVEL: '{ROOT: INFO, com.sap.cloud.sdk: DEBUG}'
and logging level to DEBUG in logback
If I remove the pre-request script from the request and send it I get the 403 response and logs shows the following messages:
"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationListener","thread":"http-nio-0.0.0.0-8080-exec-4","level":"DEBUG","categories":[],"msg":"Reading user principal"
"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationListener","thread":"http-nio-0.0.0.0-8080-exec-4","level":"DEBUG","categories":[],"msg":"Destroying Authorization as it is end of request." }
"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationService","thread":"http-nio-0.0.0.0-8080-exec-4","level":"DEBUG","categories":[],"msg":"Destroying Authorization JWT Token." }
As the other answers focus on the app to S/4 communication and you adjusted your question to make clear that you mean the User (e.g. Postman) to app communication I'll provide some additional information.
As mentioned by the other answers the CSRF handling to the S/4 system (or any OData endpoint) is automatically handled on side of the OData VDM.
What you are now encountering is the secure default configuration of the SAP Cloud SDK Maven Archetypes, which have the RestCsrfPreventionFilter
activated by default.
This filter automatically protects all non-GET
endpoints from CSRF by requiring you to fetch a CSRF Token prior to your request which you then provide.
This is completely unrelated to the OData VDM call to the S/4 system in the background.
To remedy your problems there are now three next steps:
GET
endpoint instead of POST
RestCsrfPreventionFilter
temporarily from your web.xml
RestCsrfPreventionFilter
: https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/catalina/filters/RestCsrfPreventionFilter.html