This is a duplicate of #1822, and similar to the issue with xhr sandbox in #1016.
We use the Authorization header to pass our JWT token to our backend API. We use fetch, with mode: 'cors', and whitelist the Authorization header in the preflight response using access-control-allow-headers.
This works as expected under normal conditions, and chrome will issue a preflight and pass our Authorization header in the subsequent GET.
However, in hammerhead, the header is stripped out in transform.ts/transformAuthorizationHeader().
The server request then gets a 401, as it's unauthorized, and hammerhead gives a 222 response to the browser.
Curiously, there also doesn't appear to be a preflight, although both chrome and firefox issue one without the proxy in place. hammerhead should issue a preflight, as "the request includes any headers other than those which the Fetch spec defines as being a CORS-safelisted request-header"
The fix in #1016 was to prefix the header with AUTHORIZATION.valuePrefix, then strip it out during the header transforms. It looks like this would apply equally well to fetch as it does to xhr.
We also can't seem to find any combination of fetch params that will force hammerhead to issue a preflight.
@matthias-b
We need some time to research the issue. We will get back to you as soon as we make progress.
I cannot reproduce the behavior you described. Could you please modify my test server to show me the problem?
Server for reproducing:
const http = require('http');
http
.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'content-type': 'text/html' });
res.end(`
<body>
<pre></pre>
<script>
fetch('http://localhost:2020/fetch', { method: 'GET', headers: { 'Authorization': '123' }, mode: 'cors' })
.then(r => r.json())
.then(j => document.body.firstElementChild.textContent = j.headers);
</script>
</body>
`);
} else
res.end();
})
.listen(2019, () => console.log('Please open http://localhost:2019'));
http
.createServer((req, res) => {
if (req.url === '/fetch') {
if (req.method === 'OPTIONS') {
res.writeHead(200, {
'access-control-allow-origin': 'http://localhost:2019',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'Authorization'
});
res.end();
} else {
res.writeHead(200, {
'content-type': 'aplication/json',
'access-control-allow-origin': 'http://localhost:2019'
});
res.end(JSON.stringify({ headers: JSON.stringify(req.headers, null, ' ') }));
}
} else
res.end();
})
.listen(2020);
It seems to be a combination of using a new Request object, and setting the cache option. Replace line 11 with the following, and it will strip the authorization header.
fetch(new Request('http://localhost:2020/fetch', { method: 'GET', headers: { 'Authorization': '123' }, mode: 'cors' }), {cache: 'no-cache'})
Thank you for the additional information. I've reproduced this issue.
Thanks @LavrovArtem for the fix. I see you made it part of Sprint which is due on 27th May so a new version will be available on 27th? Is that assumption correct? Cheers
@LavrovArtem thanks for such a prompt fix! You are awesome.
After much trial and error, we implemented a simple workaround in the meantime, shifting our cache setting from the fetch to the request, like so:
fetch(new Request('http://localhost:2020/fetch', { method: 'GET', headers: { 'Authorization': '123' }, mode: 'cors', cache: 'no-cache' }))
@behlrattan @matthias-b yes, we are going to release a new version.
This thread has been automatically locked since it is closed and there has not been any recent activity. Please open a new issue for related bugs or feature requests. We recommend you ask TestCafe API, usage and configuration inquiries on StackOverflow.
Most helpful comment
Thank you for the additional information. I've reproduced this issue.