Recently, I was working on a co-innovation project with one local partner in China. They will provide a Face Recognition solution which consists of a set of hardware & software. Once a person is recognized, the partner software will consume SAP Marketing Cloud contact creation API to create a contact instance in the system.
Since we need to deliver this demo in SAP Cloud Forum site in Shanghai, and it is assumed that there will be lots of guests who would like to try this solution which leads to the possibility that multiple contact creation request would be sent to Marketing Cloud system simultaneously. As a result as one of demo preparation steps, I would like to generate a large number of concurrent Marketing Cloud contact creation request using jMeter and measure its performance.
As it’s not a big deal to achieve the Marketing Cloud contact creation API in Postman, the same idea would be applied in jMeter as well: wrap two HTTP request within a Simple Controller, one request responsible for fetch XSRF token and the other for the real creation call. Once executed the jMeter project, the first call succeeds as my expectation – the token is retrieved successfully and available in HTTP response header field “x-csrf-token”.
Unfortunately the second HTTP request fails with error message “CSRF token validation failed”.
This error really confused me as I have already got the correct token from server, why validation still failed?
I searched in Google and found this graph from SAP website.
It demonstrates the Orchestration among client, service provider and identify provider for the service consumption scenario. Although in the diagram it is SAP Cloud Platform which plays the role of Service provider, not Marketing Cloud, however the logic is exactly the same.
This picture gives me a hint so I open Chrome development tool to inspect the network roundtrip when I access Marketing Cloud Fiori Launchpad, and soon I realized it is exactly follows the six steps described in the architecture image above.
Step1：I try to access Marketing Cloud Fiori Launchpad with Chrome – the access request is sent to Marketing Cloud as Service provider.
Step2: Marketing Cloud redirects this request to IDP with a HTTP 302 redirect. In my case there is a mutual trust preconfigured between Marketing Cloud and SAP ID service(that is, account.sap.com).
Step3: IDP(account.sap.com) is responsible for the actual user authentication, and returns a HTML page containing a form for user to authenticate with their user and password.
There are indeed far more information under the hood than the just two input fields as visible in the page.
Those hidden fields could be found in Chrome development tool by inspecting the source code of HTML form returned by SAP ID service.
All of them are generated by SAP ID service in the server side.
Step4: After users type their user and password and click log in button, the credentials will be sent back to IDP along with the hidden fields introduced in step3. All those fields will be involved in IDP authentication in server side, and could be observed in Chrome network tab.
Step5: IDP finishes the authentication, and issues an assertion back in HTTP response header field “SAMLResponse”.
Since this is based on SAML2.0 protocol, so we can get its XML source by a BASE64 decode. Below is my SAML assertion opened in browser:
With this assertion, end user can access the resource from Marketing Cloud now as the last step in the digram.
With all above learnings in mind, I find the cure for token validation failure soon. The complete jMeter project file (.jmx) could be found from my github.
This time the construction of jMeter project exactly abides by the six steps:
Compared with my original jMeter project, two new steps are added, highlighted with red color above.
1. Create five separate Regular Expression Extractor to parse the five parameters necessary for IDP server authentications from hidden fields in HTTP response form body:
now the five fields are stored as jMeter variables and could be used by the subsequent HTTP request.
2. use the five hidden fields plus user name & password to submit authentication request to SAP ID service.
Once log in succeeds, the cookie is set accordingly.
3. Now it’s time to fetch CSRF token. The essential point is here: for the subsequent creation API call, it is NOT enough to only submit CSRF token fetched from this step to Marketing Cloud, which will ends up with CSRF token validation failure introduced in the beginning of this part. Instead, the two cookies highlighted below MUST also be included within HTTP request as well.
This is the reason why I store the two cookies as jMeter variable here.
4. In the last step, as I just emphasized, pass the two cookies got from CSRF token fetch step to server:
And this time no CSRF token validation failure – contact creation is successfully done:
Finally I can issue some parallel contact creation request using this jMeter project. Below test result shows it takes approximately 1 seconds to create one contact in Marketing Cloud.
The created contact data in Marketing Cloud – I hard coded first name as “Jerry” and last name “Wang” and use jMeter random generator to ensure the created contact look not so boring :)
In the end I also figured out the first two HTTP requests contained in my jMeter project, that is, “get redirected Form data” and “login”, are actually optional. Only keep in mind to store cookies got from CSRF token fetch step and use them in creation API call, and that’s enough.
You can find the source code of this more simplified version here.
Last but not least, our demo in SAP Cloud Forum in Shanghai was very successful.
Here below is the Marketing Cloud Fiori Launchpad in my laptop:
And here is the Launchpad projected in the big screen in the on-site demo:
I have configured the refresh interval in contact tile as 10 seconds, so once guests in the Cloud Forum on-site demo passed facial recognition, this number just increases to give all guests a hint about the total number. At that day we have totally 276 guests who tested the solution and expressed their interests with our demo :)