这是indexloc提供的服务,不要输入任何密码
Skip to content

Emulated Hosting redirects to Cloud Functions erroneously combines Set-Cookie headers #2931

@adam-remotesocial

Description

@adam-remotesocial

[REQUIRED] Environment info

firebase-tools: 8.18.1

Platform: OSX

[REQUIRED] Test case

Write a function that sets multiple cookies:

exports.myFunc = require('firebase-functions').https.onRequest((req, res) => {
    res.setHeader('Set-Cookie', [
        'some=cookie',
        'other=one'
    ]);
    res.statusCode = 200;
    res.end('');
});

Use Hosting as a proxy to the function. firebase.json:

{
    "hosting": {
        "rewrites": [
            { "source": "/wherever", "function": "myFunc" }
        ]
    }
}

[REQUIRED] Steps to reproduce

Hit the proxied function URL: http://myhostingsubdomain.blah.com/wherever. Check the response headers in Chrome DevTools and check the Application Cookies - only the first will be registered.

[REQUIRED] Expected behavior

The response should include multiple Set-Cookie headers - one for each cookie. Browsers (including Chrome) don't handle a single Set-Cookie header with multiple cookies in it and will ignore all but the first cookie (especially with cookie fields like Expires which can include commas in them). This prevents usage like https://dev.to/johncarroll/how-to-share-firebase-authentication-across-subdomains-1ka8 where your function has to be hosted at the same origin as your Hosting sites (so cloudfunctions.net/blah is no good).

I expect to see response headers like:

Set-Cookie: some=cookie
Set-Cookie: other=one

[REQUIRED] Actual behavior

The response has a single header like:

set-cookie: some=cookie, other=one

Cause

node-fetch is being used incorrectly, indirectly from lib/hosting/proxy.js via lib/apiv2.js's Client::request. See node-fetch/node-fetch#582 for a similar case. The headers are being extracted from proxyRes.response.headers in lib/hosting/proxy.js:

for (const [key, value] of proxyRes.response.headers) {
    res.setHeader(key, value);
}

But because node-fetch stringifies the incoming response headers (which for an Array, effectively concatenates them with commas), we lose our array of headers in the proxied response. The recommended approach is to use response.headers.raw() to grab those raw Array values. When I make that change in lib/hosting/proxy.js, it works:

for (const [key, value] of Object.entries(proxyRes.response.headers.raw())) {
    res.setHeader(key, value);
}

I have no yet validated whether the same problem happens in production or not. Really hoping it doesn't 🤞
UPDATE: Production is fine! This is an emulator-only problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions