How to get/set multiple 'Set-Cookie' Headers using Fetch API? - javascript

As you might know, RFC 6265 indicates that it is allowed to have multiple headers with the Set-Cookie name.
However, Fetch API doesn't allow to do that because all the methods exposed by its Headers interface (including get(), set(), append(), entries() and all the rest) have been implemented to merge the values of all the headers with the same name into a single header separated by commas.
For example, if we do this:
var headers = new Headers();
headers.append('content-type', 'text/plain');
headers.append('set-cookie', 'test1=v; Max-Age=0');
headers.append('set-cookie', 'test2=v; Max-Age=0');
headers.append('set-cookie', 'test3=v; Max-Age=0');
and then we try to read the set-cookie values using get('set-cookie'), or by iterating the headers variable using entries(), we get this:
'set-cookie' : test1=v; Max-Age=0, test2=v; Max-Age=0, test3=v; Max-Age=0
Please notice that the same wrong behaviour also happens if we try to read or manipulate an existing response object having multiple headers with the same name (i.e. created by other frameworks that arguably support such allowed behavior): in other words, it seems like the Fetch API is completely unable to properly deal with such scenario.
Now, while this behavior is desired for some headers, such as Accept, the Set-Cookie header is not parsed correctly by most browsers (including Chrome and Firefox), thus resulting in cookies not being correctly set.
Is that a known bug? If that's the case, is there an usable workaround that can be used to overcome this?

This is a known "issue" with the standard. It's actually the first note of the Fetch API standard in the Headers section:
Unlike a header list, a Headers object cannot represent more than one Set-Cookie header. In a way this is problematic as unlike all other headers Set-Cookie headers cannot be combined, but since Set-Cookie headers are not exposed to client-side JavaScript this is deemed an acceptable compromise. Implementations could chose the more efficient Headers object representation even for a header list, as long as they also support an associated data structure for Set-Cookie headers.
You can read more or even raise your own issue in the spec's repo.
There are already a few issues discussing the Set-Cookie case at length though:
https://github.com/whatwg/fetch/issues/973
https://github.com/whatwg/fetch/issues/506
https://github.com/whatwg/fetch/issues/189
https://lists.w3.org/Archives/Public/www-archive/2016Jan/thread.html
You mentioned using workarounds, but this really depends on your use-case.
The note mentions using a secondary structure to handle those.
If you really want to store those cookies in a Headers object, you could add custom headers to store them:
new Headers([
['X-MyOwn-Set-Cookie-1', 'cookie1=value1'],
['X-MyOwn-Set-Cookie-2', 'cookie2=value2']
]);
Obviously, this is not an acceptable solution for the standard, but maybe your practical considerations might be in line with such a compromise.
As pointed out by this note and #Barmar in the comments, you usually use Set-Cookie from the server, not the front-end.
For instance, there's no problem setting multiple Set-Cookie with express:
test.js
const express = require('express');
const app = express();
const cookies = [
{ key: 'cookie1', value: 'value1' },
{ key: 'cookie2', value: 'value2' },
];
app.get('*', (req, res) => {
console.log(req.url);
for (const { key, value } of cookies) {
res.cookie(key, value, { expires: new Date(Date.now() + 1000 * 60), httpOnly: true });
}
res.status(200).send('Success');
});
app.listen(3000, () => console.log(`Listening on http://localhost:3000/`));
Terminal 1
$ node test.js
Listening on http://localhost:3000/
Terminal 2
$ curl -v http://localhost:3000/
[...]
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Set-Cookie: cookie1=value1; Path=/; Expires=Tue, 04 Aug 2020 19:45:53 GMT; HttpOnly
< Set-Cookie: cookie2=value2; Path=/; Expires=Tue, 04 Aug 2020 19:45:53 GMT; HttpOnly
< Content-Type: text/html; charset=utf-8
[...]

Related

When trying to register a user responding with "unauthorized"

What is my Problem:
I'm making an vue3 app where the login and registration should be done over back4app.
So i initialize the connection as early as possible with the code below:
Parse.initialize(
config.back4app_applicationId,
config.back4app_clientKey
)
Parse.serverURL = config.back4app_url
After this code ran there is a successful health request to the back4app-Servers
And here is the code used for sign Up:
const parseUser = new Parse.User()
parseUser.set("username", userData.username)
parseUser.set("email", userData.email)
parseUser.set("password", userData.password)
try {
await parseUser.signUp()
} catch (error) {
console.error("error: ", error)
}
When the code runs the site is sending an request to the back4app server. Respon below
Response-body:
unauthorized
Response-headers:
access-control-allow-credentials: true
access-control-allow-headers: DNT, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, X-Application-ID, X-Access-Token, X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-CSRF-Token, X-Apollo-Tracing, X-Parse-Client-Key, X-Parse-Installation-Id
access-control-allow-methods: GET, HEAD, OPTIONS, POST, PUT, DELETE
access-control-allow-origin: https://localhost:3000
access-control-expose-headers: X-Parse-Job-Status-Id
access-control-max-age: 1728000
content-length: 24
date: Wed, 28 Jul 2021 10:23:10 GMT
server: nginx/1.18.0 (Ubuntu)
via: 1.1 7fcb41b117930690c299be9cec4a977a.cloudfront.net (CloudFront)
x-amz-cf-id: AX6MG8omTAxfGPQUHUR4SkRnWW9gp33_kqJHXgEFv9eIATnI1muxyA==
x-amz-cf-pop: FRA6-C1
x-cache: Error from cloudfront
x-powered-by: Express
What have i tried:
I tried to run the code on a different PC
run the code on a site where the domain has a (not self-signed) HTTPS certificate
giving parse the master key on initiation of my application
different browsers
searching for solution in back4app and parse docs
Changing the public class level permissions for the Userclass
Hopefully i supplied all necessary information for the problem. I'm pretty lost what could be the error here and I'm very grateful for every answer.
The Error was me using the clientkey instead of the javascriptkey.
Thanks to #DaviMacêdo for providing the answer.

If I pull a url with automatic redirects from a .json, how can I make the output the page that I would have been redirected to? - Javascript [duplicate]

I was wondering if anyone knew how to handle redirects with the Request npm from sites such as bitly or tribal or Twitter's t.co URLs. For example, if I have web page that I want to scrape with the Request npm and the link I have to get to that page is a bity or shortened URL that is going to redirect me, how do I handle those redirects?
I found that the Request npm has a "followRedirect" options set to true by default. If I set that to false I can get the next link that the page will redirect me to by scraping that page that is returned, but that isn't the best because I don't know how many redirects I am going to have to go through.
Right now I am getting a 500 error. When I have "followRedirect" set to true. When I have "followRedirect" set to false, I can get each redirect page. Again, I don't know how many redirect pages I will have to go through. Code is below:
var options = {
followRedirect: false
};
request('http://t.co/gJ74UfmH4i', options, function(err, response, body){
// when options are set I get the redirect page
// when options are not set I get a 500
});
At first, you need to get the last redirect url, using followAllRedirects: true parameter
request('http://t.co/gJ74UfmH4i', {
method: 'HEAD',
followAllRedirects: true
}, function(err, response, body) {
var url = response.request.href
})
>
The second part is making request to final url, with some browser-like headers
request(url, {
headers: {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.46 Safari/537.36"
},
}, function(err, response, body) {
//here is your body
})
The Request package follows HTTP 3xx redirects by default but the URL you are using is returning an HTTP 200 with a META REFRESH style of redirect. I'm not sure if Request supports this particular style of redirect so you may need to parse the response and follow it manually.
GET http://t.co/gJ74UfmH4i HTTP/1.1
HTTP/1.1 200 OK
cache-control: private,max-age=300
content-length: 208
content-type: text/html; charset=utf-8
date: Fri, 28 Aug 2015 16:28:59 GMT
expires: Fri, 28 Aug 2015 16:33:59 GMT
server: tsa_b
set-cookie: muc=b0a729d6-9a30-466c-9cd9-57306369613f; Expires=Wed, 09 Aug 2017 16:28:59 GMT; Domain=t.co
x-connection-hash: 28133ba91da8c83d45afa434e12f8a72
x-response-time: 9
x-xss-protection: 1; mode=block
<noscript><META http-equiv="refresh" content="0;URL=http://nyti.ms/1EmZJhP"></noscript><title>http://nyti.ms/1EmZJhP</title><script>window.opener = null; location.replace("http:\/\/nyti.ms\/1EmZJhP")</script>
One possible route to understanding the issue would be to use a function for followRedirect to see if you can find out where it's failing.
From the README:
followRedirect - follow HTTP 3xx responses as redirects (default: true). This property can also be implemented as function which gets response object as a single argument and should return true if redirects should continue or false otherwise.

Get data with $resource from Flask API in AngularjJS

I'm using a Flask rest api where I've a method that I call from AngularJS with $resource factory.
I'm using now data to try it, this is the simplified code in the server:
return jsonify({'teacher': 'Tom'})
And in AngularJS, in a controller where I want to get data:
var teacher = $resource('http://localhost:8001/entities/teacher/:id');
teacher.get({id: 1}).$promise.then(function(teacher) {
// success
$scope.teacher = teacher;
}, function(errResponse) {
// fail
console.log('fail')
console.log(errResponse)
});
And this is the log in web browser console:
fail
Object { data: null, status: -1, headers: fd/<(), config: Object, statusText: "" }
But however I can see the request in network console with data in response with 200 status code, because of this I think that I don't know how read this data from $resource response, I've search info about this but I don't know how if I can see the data the resource give mi a fail error.
Any idea?
I know that I should to have this code in a service in another file, it's only a sample to find the solution.
Thanks!
Update:
I think that the problem is in the headers that I received from the server. If I try to use $http with a url that I found as example this work perfect, but when I try the same with my own server this fail even though I can see the response (and it's fine in the network console and it's fine with curl test) I think that the problem is in the headers, maybe $http or $resource needs a specific headers. I' ve updated the original question, I hope that someone can help me. I'm sure that the response is application/json.
This is the test url:
http://jsonplaceholder.typicode.com/users
and the header that is returned is:
Cache-Control
public, max-age=14400
Content-Encoding
gzip
Content-Type
application/json; charset=utf-8
Date
Mon, 03 Oct 2016 07:43:11 GMT
Etag
W/"160d-MxiAGkI3ZBrjm0xiEDfwqw"
Expires
Mon, 03 Oct 2016 11:43:11 GMT
Pragma
no-cache
Server
cloudflare-nginx
Vary
Accept-Encoding
Via
1.1 vegur
X-Firefox-Spdy
h2
X-Powered-By
Express
access-control-allow-credentials
true
cf-cache-status
HIT
cf-ray
2ebec303f9f72f7d-MAD
x-content-type-options
nosniff
And my header is:
Cache-Control
no-cache
Content-Length
35
Content-Type
application/json
Date
Mon, 03 Oct 2016 08:26:00 GMT
Expires
Fri, 01 Jan 1990 00:00:00 GMT
Link
Server
Development/2.0
I'm using a Google App Engine developer Server (I don't know if this is relevant).

Request npm: Handling Redirects

I was wondering if anyone knew how to handle redirects with the Request npm from sites such as bitly or tribal or Twitter's t.co URLs. For example, if I have web page that I want to scrape with the Request npm and the link I have to get to that page is a bity or shortened URL that is going to redirect me, how do I handle those redirects?
I found that the Request npm has a "followRedirect" options set to true by default. If I set that to false I can get the next link that the page will redirect me to by scraping that page that is returned, but that isn't the best because I don't know how many redirects I am going to have to go through.
Right now I am getting a 500 error. When I have "followRedirect" set to true. When I have "followRedirect" set to false, I can get each redirect page. Again, I don't know how many redirect pages I will have to go through. Code is below:
var options = {
followRedirect: false
};
request('http://t.co/gJ74UfmH4i', options, function(err, response, body){
// when options are set I get the redirect page
// when options are not set I get a 500
});
At first, you need to get the last redirect url, using followAllRedirects: true parameter
request('http://t.co/gJ74UfmH4i', {
method: 'HEAD',
followAllRedirects: true
}, function(err, response, body) {
var url = response.request.href
})
>
The second part is making request to final url, with some browser-like headers
request(url, {
headers: {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.46 Safari/537.36"
},
}, function(err, response, body) {
//here is your body
})
The Request package follows HTTP 3xx redirects by default but the URL you are using is returning an HTTP 200 with a META REFRESH style of redirect. I'm not sure if Request supports this particular style of redirect so you may need to parse the response and follow it manually.
GET http://t.co/gJ74UfmH4i HTTP/1.1
HTTP/1.1 200 OK
cache-control: private,max-age=300
content-length: 208
content-type: text/html; charset=utf-8
date: Fri, 28 Aug 2015 16:28:59 GMT
expires: Fri, 28 Aug 2015 16:33:59 GMT
server: tsa_b
set-cookie: muc=b0a729d6-9a30-466c-9cd9-57306369613f; Expires=Wed, 09 Aug 2017 16:28:59 GMT; Domain=t.co
x-connection-hash: 28133ba91da8c83d45afa434e12f8a72
x-response-time: 9
x-xss-protection: 1; mode=block
<noscript><META http-equiv="refresh" content="0;URL=http://nyti.ms/1EmZJhP"></noscript><title>http://nyti.ms/1EmZJhP</title><script>window.opener = null; location.replace("http:\/\/nyti.ms\/1EmZJhP")</script>
One possible route to understanding the issue would be to use a function for followRedirect to see if you can find out where it's failing.
From the README:
followRedirect - follow HTTP 3xx responses as redirects (default: true). This property can also be implemented as function which gets response object as a single argument and should return true if redirects should continue or false otherwise.

$.getJSON fails on a successful http request

I'm calling the method in my page:
var dfd = $.Deferred(
$.getJSON(serviceAddress)
.done(function (result, status) {
bo.BusinessObject.DtosToaKoArray(result, resultList);
dfd.resolve(resultList);
})
.fail(function (result, status) {
logger.logError(result);
dfd.reject(result);
}));
return dfd;
After calling the JSON, the firebug shows that HttpRequest was successfull and the response header is like:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcV29ya3NwYWNlc1xNZWhyYW5cSW5mcmFzdHJ1Y3R1cmVcTWFpblxTb3VyY2VcSW5mcmFzdHJ1Y3R1cmVcU291cmNlXENhcmFuZS5HYWxheHkuV2ViQXBpXGFwaVxEYXRhUHJvdmlkZXJcTGlzdFNlcnZpY2Vc?=
X-Powered-By: ASP.NET
Date: Sun, 04 Aug 2013 05:57:39 GMT
Content-Length: 6684
but the problem is that instead of done callback, the fail callback is called with this result:
Object { readyState=4, status=404, statusText="error"}
What is wrong about my call that fails the successful http request?
Edit1.
My website (MyApp.Web) is on localhost:2771 and the calling service is in another project (MyApp.WebApi) on the localhost:4143
You will definitely run into some hurdles trying to make cross-origin requests. That includes port-to-port.
The solutions also depend on the service and what features it supports.
JSONP (or, JSON with Padding)
The service would need to accept a callback and output the JSON wrapped in a function call:
// http://...?callback=completeRequest
completeRequest(["json", "data"]);
The parameter can be a different name than callback. But, you can instruct jQuery to use it by including a placeholder parameter (...=?):
$.getJSON(serviceAddress + "?callback=?")
This can also be used in any browser as it's requested as a <script src> and the JSON will be parsed as JavaScript literals.
CORS
The service will need to support preflight, OPTIONS requests and respond with Access-Control-Allow-* headers.
Also, while most current browsers support CORS, this can be limited if you need to support older editions.
Otherwise, you'll need to create a proxy destination in your application (localhost:2771) that makes the cross-origin request server-side to the service (localhost:4143).
It might not be finding your target page and that's why you might be getting a 404 in your result object. Make sure that your serviceAddress is correct.
In addition to that When you specify: dataType: 'json' jQuery will fire the error event if the response cannot be parsed as JSON(despite the 200 reply). Make sure that the data returned from the server is valid JSON, you might want to validate the structure manually via JSONLint.
It might be any of these 2 problems.

Categories

Resources