Parameterized configuration of cors from dotenv needs full TypeScript support
Version info
node: 18
firebase-functions: 4.5.0
firebase-tools: 13.0.2
[REQUIRED] Steps to reproduce
firebase-functions lacks the correct TypeScript types to do what is stated in the documentation for parameterized configuration. If you try the example code:
// Define some parameters
const corsOrigin = defineString('CORS_ORIGIN');
export const fn = onRequest(
{ cors: corsOrigin },
(req, res) => { ... }
);
TypeScript will fail at compilation with an error like this:
Type 'StringParam' is not assignable to type 'string | boolean | RegExp | (string | RegExp)[] | undefined'.
cors is missing a unioned type for Expression<string> like the other string configurations:
cors?: string | boolean | RegExp | Array<string | RegExp>;
So it seems impossible to configure cors this way (at least from a compilation perspective - perhaps at runtime the JavaScript will still try to eval a provided StringParam using a hack like corsOrigin as unknown as string?).
Secondly, cors takes RegExp and string arrays, and it would be great to be able to configure it using those variants as well. Cors config can vary greatly based on deployment environment.
I found a few problems with this issue:
- I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
- This issue does not seem to follow the issue template. Make sure you provide all the required information.
Hello @CodingDoug. You can use corsOrigin.value() like in the following example:
const corsOrigin = defineString('*')
export const hello = onRequest({
cors: corsOrigin.value(),
}, request => {
request.res?.json({hello: 'world'});
});
Does this solve your issue?
@exaby73 No, it doesn't work because corsOrigin.value() gets executed at load time time (before the value is known), not at runtime when the function is invoked. The value() function only works at runtime. That's why the other function function configuration parameters accept a StringParam type object, so that it can hold on and wait to evaluate it later when the value is known.
Take, for example, the TypeScript definition of the HttpsOptions object passed to onCreate, and see how it takes various Expression<> type values (which is what allows StringParams):
export interface HttpsOptions extends Omit<GlobalOptions, "region"> {
omit?: boolean | Expression<boolean>;
region?: SupportedRegion | string | Array<SupportedRegion | string> | Expression<string> | ResetValue;
cors?: string | boolean | RegExp | Array<string | RegExp>;
memory?: options.MemoryOption | Expression<number> | ResetValue;
...
}
See that cors does not accept an Expression<string> unlike the other configs. That's what I'm asking to add.
I understand your use case here. I will label this as a feature request and let additional insights come in from the team. Thank you :)
Any update on this? Is there now (after a year later) a way to configure cors based on the environment?
Hi @inlined , could we maybe get something similar as you've done in #1538 here for this to be able to configure CORS options for V2 functions based on parameters?
In my case I would actually prefer something like this:
const isStaging = defineBoolean('STAGING')
const corsOptions = isStaging.value() ? ["stagingcors..."] : ["prodcors..."]
export const myFn = onCall(
{
cors: corsOptions,
},
...
)
However, I understand why that's probably not possible so I would also be fine with something like this as @CodingDoug proposed:
const corsOptions = defineString('corsoptions') // if that string can define a regex
export const myFn = onCall(
{
cors: corsOptions,
},
...
)
Strange. This issue was never escalated to the team. I can take a look.
@inlined thanks for implementing it, I happily saw this was released with https://github.com/firebase/firebase-functions/releases/tag/v6.4.0 last week, however, I am wondering how I would need to adapt to it now since it broke the existing code.
Currently I was doing something like this which worked prior to v6.4.0:
.env:
ALLOWED_CORS_ORIGINS="^https:\/\/(some|other)\.example\.com$"
export const environmentVariableAllowedCorsOrigins = defineString('ALLOWED_CORS_ORIGINS')
const corsOptions = environmentVariableAllowedCorsOrigins as unknown as string
export const myFn = onCall({
cors: corsOptions
}, ...
)
...however, this fails now since updating to [email protected] with the following message in Chrome:
Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains the invalid value '^https://(some|other).example.com$'. Have the server send the header with a valid value.
Obviously, the incorrect Access-Control-Allow-Origin header is provided to the client with response to the preflight request - it should only report the actual host but it returns the whole regex as string now.
How would I now provide such a CORS option that needs to be a regex (or multiple single strings) based on a defineString parameter?
...when I try the following (basically used from the PR's tests), it fails when starting the emulator:
ALLOWED_CORS_ORIGINS="['example.com','example2.com']"
const corsOptions = defineList('ALLOWED_CORS_ORIGINS')
export const myFn = onCall({
cors: corsOptions
}, ...
)
It fails when starting the emulator with the following error:
Serving at port XXXX
{"severity":"WARNING","message":"params.ALLOWED_CORS_ORIGINS.value() invoked during function deployment, instead of during runtime."}
{"severity":"WARNING","message":"This is usually a mistake. In configs, use Params directly without calling .value()."}
{"severity":"WARNING","message":"example: { memory: memoryParam } not { memory: memoryParam.value() }"}
SyntaxError: "undefined" is not valid JSON
@CorieW could you maybe help to shine some light on how this is intended to be used now?
Especially also the question regarding the regex possibility I mentioned in this comment https://github.com/firebase/firebase-functions/issues/1506#issuecomment-3109941919 that used to work but now broke with that change?
This is actually blocking the update to [email protected] or higher.
Hi @CorieW and @inlined , is there any update regarding the questions in my previous comment?
This is unfortunately a hard blocker for the update to [email protected] or higher as it broke existing CORS functionality.