on ‎2020 Jul 20 10:39 AM
Hi ,
Any lead on how to use service.tx(request).run(request.query) for a post call to S/4 odata.
More details
When I use service.tx(request).run(request.query) for a post call, Iam getting CSRF token invalid error in S/4 and thus 403 error in CF.
Iam able to get CSRF token with S/4 URL directly in Tcode /iwfnd/maint_service with header as [X-CSRF-TOKEN : Fetch ].
Iam not able to get CSRF token if i use post using service.tx(request).run(request.query) of SAP CAP Service. [HTTP header is not having the [X-CSRF-TOKEN : Fetch] ]
Exact question
Regards,
Karthi
Request clarification before answering.
I had the same problem when accessing the Cloud 4 Customer OData API through an imported external service. Additionally, the C4C API doens't implement HEAD.
I solved it by modifying http-client.js of @sap-cloud-sdk . I added the following intercepter at the end of the file (L195 in version 1.28.1). Note that this is targeting C4C and might need some small changes to make it generic.
function createRequestInterceptor() {
const interceptor = axios_1.default.interceptors.request.use(async (config) => {
if (config.url.startsWith('/cust/')) {
// for customer OData resources, reqrite the baseURL to point to the customer namespace
config.baseURL = config.baseURL.replace(/\/v1\/c4codataapi/, '')
}
if (['head', 'get'].includes(config.method)) {
config.headers['x-csrf-token'] = 'fetch'
}
// intercept post/patch/delete and send a head request
// to the same resource to fetch the CSRF token
// the intercepter needs to be ejected before sending the head
// request to avoid endless recursion
if (['post','delete','patch'].includes(config.method)) {
let tokenConfig = __assign({}, config)
// better would be head, but our target system (C4C) doesn't implement HEAD
tokenConfig.method = 'get'
delete tokenConfig.data
delete tokenConfig.headers['content-type']
delete tokenConfig.headers['content-length']
tokenConfig.headers.common['x-csrf-token'] = 'fetch'
tokenConfig.validateStatus = (status) => {
return status >= 200 && status < 300 || status == 400;
}
try {
axios_1.default.interceptors.request.eject(interceptor)
let tokenResponse = await axios_1.default.request(tokenConfig)
config.headers.common['x-csrf-token'] = tokenResponse.headers['x-csrf-token']
const cookies = tokenResponse.headers['set-cookie']
if (cookies) {
config.headers.Cookie = cookies.join('; ')
}
} catch (e){
if (e.isAxiosError) {
console.log(e.response.status, e.response.statusText)
} else {
console.log(e)
}
} finally {
createRequestInterceptor()
}
}
return config
})
}
createRequestInterceptor()
Someone smarter than me can probably come up with a cleaner way to add the interceptor to the http client without having to modify the @sap-cloud-sdk library. Additionally this will request a new CSRF token before every patch/post/delete instead of caching the token.
It's an extremely simple "fix" though and for my purposes it's good enough
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks Gregor, I had tried cdse but it didn't work for me and I would have to rewrite it as well to fit C4C. And honestly I find the axios interceptor a bit more elegant than rewriting the destination handling as it's completely agnostic to how axios is configured.
Anyway this works well for my use case and I meant to share this approach as an alternative.
| User | Count |
|---|---|
| 8 | |
| 5 | |
| 4 | |
| 4 | |
| 3 | |
| 3 | |
| 2 | |
| 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.