on 2024 Nov 08 2:00 PM
Hello Community,
The app contains a FileUploader element. The related backend oData service has CSRF protection enabled. To enable file upload, there is a controller extension for the FileUploader where the CSRF token is fetched and explicitly added (x-csrf-token) to the POST request header. It works correctly from SAP BAS with "Preview application".
The app is now deployed to Cloud Foundry, where it is added to a SAP Build Work Zone standard edition page. The upolad from the Build Work Zone site throws the error 403, and this is found in the approuter error log:
POST request to /sap/opu/odata/sap/Z<....> completed with status 403 The request contains an invalid x-csrf-token
I can se in the browser debugger that the request does contain the x-csrf-token parameter with a plausible value. That part looks identical as when testing from SAP BAS where the token is accepted.
I have found note 3495019 "403 error when the AppRouter generates a CSRF Token" which seems relevant. Except I am pretty sure the CSRF protection is already disabled in the xs-app.json. Here it is:
{
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"routes": [
{
"source": "^/sap/(.*)$",
"target": "/sap/$1",
"destination": "<name of my Cloud Connector destination>",
"authenticationType": "xsuaa",
"csrfProtection": false
},
{
"source": "^/resources/(.*)$",
"target": "/resources/$1",
"authenticationType": "none",
"destination": "ui5"
},
{
"source": "^/test-resources/(.*)$",
"target": "/test-resources/$1",
"authenticationType": "none",
"destination": "ui5"
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
}
]
}I did find number of similar questions, most notably:
but no solved ones, or possibly I do not understand the recommendations and need more guidance.
Please advise what to do - thank you in advance!
Request clarification before answering.
With the valuable help of SAP Support I was able to resolve the issue. It turns out the problem was not the CSRF token itself, but rather that the upload URL of the FileUploader was incomplete.
More precisely, the uploadURL was defined like this in the fragment:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:u="sap.ui.unified">
x
<u:FileUploader
id="AttaUpload"
name="AttaUpload"
uploadUrl="uploadUrl="/sap/opu/odata/sap/<MY_SERVICE>/<MyEntitySet>('{<ID>}')/<MyDependentEntitySet>"
useMultipart="false"
change="onFileChange"
afterDialogClose="afterDialogClose"
uploadStart="onUploadStart"
uploadComplete="onUploadComplete"
buttonOnly="true"
uploadOnChange="true"
sendXHR="true"
buttonText="{@i18n>new}"
icon="{sap-icon://attachment-photo}"
/>
</core:FragmentDefinition>
At runtime this produced an URL like so:
And this URL is not correct because it is relative to just the WorkZone launchpad, and not to my app.
When in reality, the correct URL has to contain the Destination Service ID for the destnation of our backend service, something like this:
https://<ourBTPAccount>.launchpad.cfapps.eu10.hana.ondemand.com/alphanumericalGUID.<destinationName>.<destinationName>/~moreRandomNumbers-~sap/opu/odata/sap/<MY_SERVICE>/<MyEntitySet>('<ID>')/<MyDependentEntitySet
I was able to resolve by picking up the current URL at runtime and setting it to the fileUploader uploadUrl, as follows:
In fragment XML:
- changed the uploadUrl in the fragment to just "/<MyEntitySet>('{<ID>}')/<MyDependentEntitySet"
In extension controller:
- declare global variable sServiceURL
- in onAfterRendering lifecycle event: capture the runtime path into sServiceURL
- in fileUploader related event (for me it was onFileChange): concatenate sServiceURL with <MyEntitySet>('<ID>')/<MyDependentEntitySet to get the full URL, and use method setUploadURL of fileUploader class to change to this new URL
Here is the abridged controller code:
sap.ui.define([
"sap/m/MessageBox"
], function (MessageBox) {
"use strict";
var sServiceUrl; // oData service URL
return {
onAfterRendering: function () {
var oView = this.getView();
/* Get service URL at runtime. This will be used in FileUploader event to complete the upload URL
including CF destination service ID. It cannot be fetched there
because at that event there is no Model defined for the view any more */
sServiceUrl = oView.getModel().sServiceUrl;
},
// FileUploader Events:
onFileChange: function (oEvent) {
var oFileUploader = oEvent.getSource();
var oFile = oFileUploader.oFileUpload.files[0];
this._file = oFile;
console.log("File selected:", oFile);
/* Destroy the header before each new uplaod.
Otherwise the parameters are just accumulating on each consecutive call
and the CSRF tokens are not recognised by SAP Server and refused */
oFileUploader.destroyHeaderParameters();
var headerParmaSlug = new sap.ui.unified.FileUploaderParameter();
headerParmaSlug.setName('slug');
headerParmaSlug.setValue(oFileUploader.getValue());
oFileUploader.addHeaderParameter(headerParmaSlug);
/* Get the CSRF token value and add as new header parameter */
this.csrfToken = this.getView().getModel().getSecurityToken();
var headerParmaCSRF = new sap.ui.unified.FileUploaderParameter();
headerParmaCSRF.setName('x-csrf-token');
headerParmaCSRF.setValue(this.csrfToken);
oFileUploader.addHeaderParameter(headerParmaCSRF);
/* Set uploadURL with the full service path. This ensures that
the destination service ID on CP will be correctly inserted into the URL */
var newUploadURL = sServiceUrl + oFileUploader.getUploadUrl();
oFileUploader.setUploadUrl(newUploadURL);
},
}
});
Again thanks to SAP Support for having pointed out the URL issue, as it never even occurred to me to look past the 403 error in the approuter log 🙂
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi!
Just a heads up - as I am also on my way having a similar issue, I found out the managed approuter does not allow / support the deactivation of `x-csrf-token` - Check out this chapter on SAP Help: https://help.sap.com/docs/cloud-portal-service/sap-cloud-portal-service-on-cloud-foundry/configure-a... (bottom):
The managed application router (HTML5 Applications Runtime) enables CSRF protection for any HTTP method that is not GET or HEAD and the route is not public.
Looks like, we've to live with that fact. However, I am having the same issue, running into a "403 - (Fortbidden)" Issue, by trying to POST something to a CPI endpoint.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello @dr_vup, thank you for commenting. My interpretation of note 3495019 (403 error when the AppRouter generates a CSRF Token) was that the CSRF protection on the approuter can be disabled.
Have you tried the xs-app.json setting recommended in the note ("csrfProtection": false) ? Did it make a difference for your issue?
| User | Count |
|---|---|
| 7 | |
| 5 | |
| 4 | |
| 3 | |
| 3 | |
| 3 | |
| 3 | |
| 2 | |
| 2 | |
| 2 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.