on 2015 Aug 17 10:25 PM
Question 1:
I have problems while using REST POST operations in ABAP report in context of the CSRF token
Background: Testing the possibilities of consuming oData services with ABAP reports and handling JSON content
Problem: I always get :
Satus: 403
Response: CSRF token validation failed finisdh
Example ABAP report:
1) first GET to fetch the token
2) make the post with HEADER parameter fetched token X-CSRF-Token
PARAMETERS: partner TYPE but000-partner,
invoice TYPE vbrk-vbeln.
START-OF-SELECTION.
TRY.
DATA: lv_service_url TYPE string,
lo_http_client TYPE REF TO if_http_client,
lo_rest_client TYPE REF TO cl_rest_http_client,
lo_response TYPE REF TO if_rest_entity,
lv_http_status TYPE i,
lv_token TYPE string.
* (1)
** GET CSRF token
cl_http_client=>create_by_url(
EXPORTING url = lv_service_url " oData service URL
IMPORTING client = lo_http_client
EXCEPTIONS OTHERS = 1 ).
CHECK sy-subrc EQ 0.
lo_http_client->request->set_content_type( 'application/json' ).
CREATE OBJECT lo_rest_client EXPORTING io_http_client = lo_http_client.
* fetching token
lo_rest_client->if_rest_client~set_request_header( EXPORTING iv_name = 'X-CSRF-Token' iv_value = 'Fetch' ).
lo_rest_client->if_rest_client~get( EXCEPTIONS OTHERS = 1 ).
CHECK sy-subrc EQ 0.
lo_response = lo_rest_client->if_rest_client~get_response_entity( ).
lv_http_status = lo_response->get_header_field( '~status_code' ).
CHECK lv_http_status EQ '200'.
" get token for POST request
lv_token = lo_response->get_header_field( 'X-CSRF-Token' ).
FREE: lo_http_client, lo_rest_client.
* (2)
** POST with CSRF token
DATA LO_REQUEST TYPE REF TO if_rest_entity.
cl_http_client=>create_by_url(
EXPORTING url = lv_service_url " oData service URL
IMPORTING client = lo_http_client
EXCEPTIONS OTHERS = 1 ).
CHECK sy-subrc EQ 0.
lo_http_client->request->set_content_type( 'application/json' ).
CREATE OBJECT lo_rest_client EXPORTING io_http_client = lo_http_client.
* build Example request data to send
DATA lv_json_post_data TYPE string.
lv_json_post_data = | \{ | &&
| "user":"{ sy-uname }", | &&
| "partner":"{ partner }", | &&
| "invoice":"{ invoice }" | &&
| \} |.
lo_request = lo_rest_client->if_rest_client~create_request_entity( ).
CHECK lo_request IS BOUND.
lo_request->set_header_field( iv_name = 'X-CSRF-Token' iv_value = lv_token ).
lo_request->set_content_type( iv_media_type = if_rest_media_type=>gc_appl_json ).
lo_request->set_string_data( lv_json_post_data ).
* POST
lo_rest_client->if_rest_resource~post( lo_request ).
* Collect response
lo_response = lo_rest_client->if_rest_client~get_response_entity( ).
lv_http_status = lo_response->get_header_field( '~status_code' ).
DATA lv_response TYPE string.
lv_response = lo_response->get_string_data( ).
CASE lv_http_status.
WHEN '201'.
* JSON to ABAP
DATA lr_json_deserializer TYPE REF TO cl_trex_json_deserializer.
CREATE OBJECT lr_json_deserializer.
DATA ls_test_content TYPE ztestrest.
lr_json_deserializer->deserialize( EXPORTING json = lv_response IMPORTING abap = ls_test_content ).
WRITE: /'CreadedBacklinkID:', ls_test_content-hashid.
WHEN OTHERS.
WRITE 😕 'Satus:', lv_http_status.
WRITE 😕 'Response:', lv_response.
ENDCASE.
CATCH cx_root.
ENDTRY.
It works without problems when I deactivate the CSRF token in the SICF for this service with parameter ~CHECK_CSRF_TOKEN = 0
When I deactivating the CSRF token there is the need to use the header parameter X-Requested-With :
lo_request->set_header_field( iv_name = 'X-Requested-With' iv_value = 'X' ).
and it also works in a browser REST test client. Problems are only visible when using the ABAP oo rest client
Question 2 :
The other problem, when the service works the response from the Gateway is XML per default and I have no clue how to say that I also want the returning entity in the server response also in JSON.
Can anyone help me here ?
Thanks in advance,
Thomas
Request clarification before answering.
Question 1: You can have a look at the framework code /IWFND/CL_OCI_REQ_PROCESSOR method /IWFND/IF_OCI_REQ_PROCESSOR~UPDATE_ENTRY, especially at the line where lo_http_wrapper->put( ).
Question 2:Add $format=json at teh URL, this will return you the data in JSON format
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
thanks for your time and help.
the mentioned methods are interesting.
regarding Question 1:
I checked the SAP methods and it seems they are going the same way :
I adapted my code to use the special /iwcor/if_rest_entity entity and the /iwcor/if_rest_client REST client but still the same.
When I check the request header of the entity object before the post it looks good :
but after calling the POST method the request entity has the following header always with error 'CSRF token validation failed'
I then tested the same token with the Chrome RESTclient and also there the token is invalid.
So it seems more a problem that the fetched token is invalid already when I call the POST request ?
But it works when I fetch a token in the Rest client and use this one - I now also realize that with the Chrome Rest Client I get always the same token as long the token is active. But the fetch method in ABAP gives me always another token which is always invalid ^^
regarding Question 2:
the problem is that I can't add $format=json in a POST request - that will end up in this error :
"code":"005056A509B11EE1B9A8FBE8A100F78B",
"lang":"en",
"value":"The Data Services Request contains SystemQueryOptions that are not allowed for this Request Type"
}
}
}
it seems specially for POST or UPDATE request we can't use the option $format=json and so we need to parse the XML with a writer to JSON by our own
This gives me a JSON response for a POST request
Thanks a lot
And regarding Question 1:
* the ABAP GET request always generates a different token - this should be always the same as long the token is valid
* testing to use the valid token from the external REST client for the POST request also ends up in the message 'invalid token'
Hi,
I had a look at your code. I can see you are calling teh below line
cl_http_client=>create_by_url( )
two times. In the first call you fetch the CSRF token and in the second call you use the previously fetched CSRF value. I think this causes the issue.
As you are instantiating the lo_http_client twice, you are creating two different sessions. Thus the CSRF from first session is invalid for the second session and you get the CSRF error.
You should reuse lo_http_client second time, instead of creating it once more.
Hopefully this would solve the CSRF issue.
Refer to the class I mentioned, exactly the same thing is happening there as well
Regards
Atanu
To post in JSON format, try the following
1.add http header Content-Type:Application/json
2.HTTP Method: POST
3. URL: http://,...../Entityset
4. Proper JSON Payload
Yes this is correct regarding the sending payload (request).
But the question was about the response - so the system gets the request and then an ID is generated for the new entry and we get a resposne with the enety and here we can parse the new ID.
The correct answer here was adding the http header key 'Accept' and then the response from the POST is also in json :
lo_rest_client->set_request_header(
EXPORTING iv_name = 'Accept'
iv_value = 'application/json' ).
I started with a scenario like that and only separated it because I thought the error is that I use the same http client. I just tested it again and yes I get the same error.
That is not the reason and also the token should be valid for 24 hours and not dependend on the session.
I also already realized that I get the same token with different browser REST clients but the ABAP rest GET token fetch method always generates a new token, I think here the problem already starts.
There are differences in the releases :
1. Release < 7.03/7.31 or the security session management is inactive: An own CSRF cookie gets generated (sap-XSRF_<SystemID>_<SAPClient>) and this CSRF token remains valid for 24 hours (86400 seconds).
2. Release >= 7.03/7.31, the validity is bound to the security session, which depends on the system parameter http/security_session_timeout value (see transaction RZ11 for details on this parameter). By default, the security session management is active in these releases."
Cross-Site Request Forgery Protection - SAP Gateway Foundation (SAP_GWFND) - SAP Library
So it depends on the session and I found also the table the session ID and the CSRF token is saved
SECURITY_CONTEXT and in the ->POST() call itself he generates a new entry in this table - when I hard change the token to the token from the new created table line it works ^^
1) after get post and before POST call
2) when I debug in POST call before method call /IWCOR/IF_REST_CSRF_HANDLER~VALIDATE_CSRF_TOKEN
system calls fuba TH_GET_SECURITY_CONTEXT_REF which returns the LINK for the newest generated token - here 2767E995468E11E5A473005056B03327
here the short extract from GET to POST with same object and only after execute the POST the second CSRF token is created somehow
lo_rest_client->get( exceptions others = 1 ).
* Get Token after GET
lv_token = lo_rest_client->get_response_header( /iwfnd/if_oci_common=>gc_xcsrf_token ).
** POST with CSRF token
* Set the CSRF token to the header
lo_rest_client->set_request_header(
exporting iv_name = /iwfnd/if_oci_common=>gc_xcsrf_token
iv_value = lv_token ).
lo_request ?= lo_rest_client->create_request_entity( ).
* set data
lo_request->set_string_data( lv_json_post_data ).
* POST / until here there is only one token in table SECURITY_CONTEXT
lo_rest_client->post( lo_request ).
Hi
I have problems while using REST POST operations in ABAP report in context of the CSRF token .
I am getting a error using the same code as 'CSRF~Token' required with response 403 .
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello Thomas,
For question 1, we had the same issue, every get call will return a new token, that's the problem, after we setting abap http client to accpet cookie, it works fine.
lo_http_client->PROPERTYTYPE_ACCEPT_COOKIE = if_http_client=>co_enabled
.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
To process the POST you need to start with the GET request and a "x-csrf-token: fetch".
And here the request header will contain a automatically created "cookie" :
And the response header will deliver the "x-csrf-token" :
And in the POST request you not only should include the "x-csrf-token" but also the content of the first request cookie :
User | Count |
---|---|
84 | |
12 | |
9 | |
8 | |
8 | |
5 | |
4 | |
4 | |
3 | |
3 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.