cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

CSRF Token Issue when calling OData Service locally in S/4HANA Cloud Public Edition

DiegoValdivia
Active Participant
0 Kudos
418

Hi guys,

I've been doing some research about calling Odata Services Locally in S/4HANA Cloud Public Edition, but have just faced some issue and was wondering if any of you could help me.

We need to Create TM Freight Orders using a job, but the only Behavior Definition related to Freight Orders I_FreightOrderTP is allowed only to read orders, but doesn't allow to create them:

DiegoValdivia_0-1729709298506.png

One solution we found is to call API_FREIGHTORDER from CPI, but man, why adding this complexity if we've been allowed during decades to do this kind of things locally using BAPIs?

Based on that, I found the following great post:

How to attach documents to a Journal Entry within SAP S/4HANA Public Cloud from ABAP. 

I was able to replicate the same steps, but using the API_FREIGHTORDER.

I created the following outbound service (plus all the Communication Arrangement stuff)

DiegoValdivia_1-1729709379711.png

I then created the following class:

 

 

CLASS y43tmcl_create_frgtorders IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.

    TRY.
*       Create Destination
        DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
                                 comm_scenario  = 'Y43TMCS_FREIGHT_ORDER_CR'              "Outbound Scenario
                                 service_id     = 'Y43TMOS_FREIGHT_ORDER_CR_REST'         "Outbound Service
                               ).

*       Create the HTTP Client
        DATA(lo_http_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
        lo_http_client->accept_cookies( abap_true ).

*       Create the HTTP Request
        DATA(lo_request) = lo_http_client->get_http_request( ).

*       -------------------------------------------
*       GET Operation - X-CSRF-TOKEN and COOKIE
*       -------------------------------------------

        DATA(lt_header_fields0) = lo_request->get_header_fields( ).

*       Get x-csrf-token
        lo_request->set_header_fields( VALUE #( (  name = 'Content-Type' value = 'application/json' )
                                                (  name = 'Accept'       value = '*/*' )
                                                (  name = 'Connection'   value = 'keep-alive' )
                                                (  name = 'x-csrf-token' value = 'fetch' ) ) ).

*       Perform the HTTP GET Request
        DATA(lo_response) = lo_http_client->execute( if_web_http_client=>get ).

*       Get the Response Header Fields and read the eTag
        DATA(lt_header_fields) = lo_response->get_header_fields( ).
        DATA: lv_Cookie TYPE string.
        LOOP AT lt_header_fields INTO DATA(ls_header_fields).
          CASE ls_header_fields-name.
            WHEN 'x-csrf-token'.
              DATA(lv_xcsrftoken) = ls_header_fields-value.
            WHEN 'set-cookie'.
              IF lv_Cookie IS INITIAL.
                lv_Cookie = ls_header_fields-value.
              ELSE.
                lv_Cookie = lv_Cookie && ';'  && ls_header_fields-value.
              ENDIF.
          ENDCASE.
        ENDLOOP.

*       -------------------------------------------
*       POST Operation - CREATE FREIGHT ORDER
*       -------------------------------------------

        lo_request->set_header_fields( VALUE #( (  name = 'Content-Type' value = 'application/json' )
                                                (  name = 'Accept'       value = '*/*' )
                                                (  name = 'x-csrf-token' value = lv_xcsrftoken )
                                                (  name = 'Cookie'       value = lv_Cookie ) ) ).

*       Set the Service Path pointing to Freight Order for which we want to get the eTag
        lo_request->set_uri_path( i_uri_path = '/SAP__self.CreateFreightOrder' ).

*       Set Body Contents to be modified
        lo_request->set_text( EXPORTING i_text = '{"TransportationOrderType":"SFO2"}' ).

*       Perform the HTTP POST Request
        lo_response = lo_http_client->execute( if_web_http_client=>post ).

*       Get the status of the call
        DATA(ls_status) = lo_response->get_status( ).

*       Write the results on the Console Log
        out->write( 'Status' ).
        out->write( ls_status ).

        DATA(lv_response) = lo_response->get_text(  ).
        out->write( lv_response ).

      CATCH cx_root INTO DATA(lo_root).
         out->write( lo_root->get_text(  ) ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

 

 

 

 Problems I found

When I perform the Post Operation (using following code)

 

 

lo_request->set_uri_path( i_uri_path = '/SAP__self.CreateFreightOrder' ).
lo_request->set_text( EXPORTING i_text = '{"TransportationOrderType":"SFO2"}' ).
lo_response = lo_http_client->execute( if_web_http_client=>post ).

 

 

 The Service Responds with 403 Forbidden

 

Solution?

The only solution I have found so far is by modifying the code. When I'm going to call the Get Operation, to get the CSRF Token, I first set the URI Path using the path of the Action itself

 

 

lo_request->set_uri_path( i_uri_path = '/SAP__self.CreateFreightOrder' ).

 

 

I'm aware this is not correct in terms of Service development, we shouldn't perform a Get operation on an Action, but this is the only way in which SAP doesn't return a 403 Forbidden error and allows me to create the order.

The problem with above solution is that, such Get Operation on an action triggers a Gateway error in the system.

DiegoValdivia_2-1729710153574.png

Such error doesn't stop my logic in any way. But since this program is aimed to create Freight Orders massively, I would be adding a ton of Gateway errors per day in the system.

 

Final thoughts

  • Somehow SAP doesn't recognize the CSRF token generated by my first Get Operation.
  • Somehow SAP "forgets" about the CSRF token at the moment I set the URI related to the Post Action

Some additional comments:

  • Freight Order API doesn't allow to call Create operation directly. The only way to create Freight Orders is by calling the CreateFreightOrder action
  • All this process works perfectly in Postman.

 

If you have any advice here, I would really appreciate it.

Accepted Solutions (0)

Answers (1)

Answers (1)

Johnny_B_
Active Participant
0 Kudos

Hi Diego,

I think you are on the right track by setting the URI patch when fetching the csrf-token.

What I do no understand is that you say "we shouldn't perform a Get operation on an Action".

If you do it in Postman manually, how would you do it? I always first send a GET to the service so to fetch the csrf-token, the request goes here:

GET: https://my406xx-api.s4hana.cloud.sap/sap/opu/odata/sap/API_SUPPLIERINVOICE_PROCESS_SRV/A_SupplierInv...

and right after that I can post and create a supplier invoice in this example:

POST: https://my406566-api.s4hana.cloud.sap/sap/opu/odata/sap/API_SUPPLIERINVOICE_PROCESS_SRV/A_SupplierIn...

Kind regards,

Johannes