Skip to Content
Technical Articles

Your SAP on Azure – Part 18 – The story of a missing CSRF token

I like IT puzzles. Especially when they relate to SAP. When I find an interesting topic, I won’t leave my desk until I find the solution. So when my colleague Roman Broich wrote me a short message that he was fighting with OData and SAP, my eyes widened and my breath accelerated. I already knew it would absorb me completely. “It can’t be trivial, otherwise he wouldn’t even mention about it” – I thought. So I just wrote a quick text to my wife that she shouldn’t disturb me when she’s back from work, I prepared a fresh cup of coffee and I was ready to go.

Roman’s tutorial about connecting IoT devices with SAP can be found here:
https://github.com/ROBROICH/SAP_AND_AZURE_IOT_DEM

I highly recommend to check it out!

The problem was in fact very irritating. We tried to use an Azure Logic App to create a record in SAP using an OData service, but every time we got an error saying “CSRF token validation failed”.

A quick internet search confirmed my suspicion that we’re not the only ones facing the issue. I found SAP Note 2597429 – “CSRF token validation failed for Fiori / OData PUT or POST field update or Use as Request” that referenced a great blog “Issues with CSRF token and how to solve them” and I thought the mystery is solved. I even felt slightly disappointed. I modified my Logic App to retrieve the missing CSRF token and send it with the next call by adding the following headers:

HTTP GET:
X-CSRF-Token: fetch

HTTP POST:
X-CSRF-Token: @triggerOutputs()['headers']['X-CSRF-Token']

Can you imagine how surprised I was when I checked the outcome and the issue persisted?

After double checking that I passed the correct token, I started to look for another solution. I read the above-mentioned SAP Note for the third time, but it didn’t bring me closer to a solution. “Let’s give it a try with Postman”, I thought, and I quickly prepared two requests:

Although I received the Internal Server Error when sending the POST request (due to lack of the message body) the OData service did not complain about a missing or incorrect CSRF token. When I looked at the message headers, I noticed a small difference.

In the GET request, we can clearly see the Authentication header, which is missing in the second execution. The POST call instead has a Cookie header which means, that Postman is smart and tries to re-use the connection using the cookies sent by the SAP system. That would make sense – each time the user is authenticated, the SAP backend generates a new CSRF token and that’s why during the second call in my Logic App I’m getting the error. As Postman is re-using cookies, it doesn’t send the authentication header again, so the token stays the same.

OK, that’s quite easy to do in Logic Apps. I added a new Cookie parameter and removed the authentication. My second request looked as follows:

I executed the app, but unfortunately, I received an even more surprising message:

At this moment I got very confused. I couldn’t find any meaningful difference when comparing the Postman and Logic App execution. If the Cookies are set, why am I not authorized?

Around an hour later I saw daylight. In the response header in the Postman I could see three entries for Set-Cookie, however the Azure Logic App displayed only one.

Postman:

Logic Apps:

The difference was that in the second call Postman used only the last entry, while Logic App concatenated all of them. You can’t see that in the print screen, but the full cookie looked like this:

"Set-Cookie": "sap-usercontext=sap-client=100; path=/,MYSAPSSO2=AjQxMDMBABhCAEoAQQBSAEsATwBXAFMASwBJACAAIAACAAYxADAAMAADABBBADQASAAgACAAIAAgACAABAAYMgAwADEAOQAwADUAMQA0ADEANgAyADMABQAEAAAACAYAAlgACQACRQD%2fAPswgfgGCSqGSIb3DQEHAqCB6jCB5wIBATELMAkGBSsOAwIaBQAwCwYJKoZIhvcNAQcBMYHHMIHEAgEBMBowDjEMMAoGA1UEAxMDQTRIAggKIBgQIiFCATAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwNTE0MTYyMzE2WjAjBgkqhkiG9w0BCQQxFgQU%21Y1ABVRurueVQzu7O%21QNWsDW%21oAwCQYHKoZIzjgEAwQuMCwCFG%2fwDay0G824VxEq3PPyDExODJYMAhR1G5583Ka1HrE%21DH18AZp56kyRXg%3d%3d; path=/; domain=.166.35.222,SAP_SESSIONID_A4H_100=RXFEn_rQNhKtrPyXBy8RKJORFQ12ZBHpjyQADTo6P24%3d; path=/"

The entries were comma- separated. Initially I planned to use a Substring operation to chunk the third part of the Cookie, but unfortunately, the MYSAPSSO2 length was different in each execution so that was not an option. I could write a small Function App or use an Integration Account to do some string transformation, but I thought there must be a simpler solution.

And there it was! I noticed that within a single Set-Cookie header each parameter is separated by a semicolon and not a comma. My next modification was to replace all commas with semicolons and let SAP decide what it looks for. I set the Cookie parameter to the following:

@{replace(triggerOutputs()['headers']['Set-cookie'], ',', ';')}

And voila! No authentication error!

The 500 Internal Server Error received from the server was caused due to empty payload, but that was something that I could fix easily. In addition I entered Content-Type header set to application/atom+xml; charset=utf-8 and deleted unnecessary parameters from the URL (otherwise you’ll get UnsupportedMediaType exception). I clicked Run.

Execution succeeded! The 201 status code mean that the record has been created in the SAP system.

Be the first to leave a comment
You must be Logged on to comment or reply to a post.