Zeit (Vercel) Now serverless authenticated requests failing because of CORS
I have pretty much similar issues with CORS and Vercel serverless function.
After lots of try → failed process I just found solutions for this.
Solutions
tldr
The simplest solution, just using micro-cors.
And having an implementation something like;
import { NowRequest, NowResponse } from '@now/node';
import microCors from 'micro-cors';
const cors = microCors();
const handler = (request: NowRequest, response: NowResponse): NowResponse => {
if (request.method === 'OPTIONS') {
return response.status(200).send('ok');
}
// handle incoming request as usual
};
export default cors(handler);
Longer version, but without any new dependency
using vercel.json
to handle request headers
vercel.json
{
"headers": [
{
"source": "/.*",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Access-Control-Allow-Headers",
"value": "X-Requested-With, Access-Control-Allow-Origin, X-HTTP-Method-Override, Content-Type, Authorization, Accept"
},
{
"key": "Access-Control-Allow-Credentials",
"value": "true"
}
]
}
]
}
After self tried, there are 2 keys important in an above setting,
- You must set
Access-Control-Allow-Origin
as what you want - In
Access-Control-Allow-Headers
you must includeAccess-Control-Allow-Origin
into its value.
then in serverless function, you still need to handle pre-flight request as well
/api/index.ts
const handler = (request: NowRequest, response: NowResponse): NowResponse => {
if (request.method === 'OPTIONS') {
return response.status(200).send('ok');
}
// handle incoming request as usual
};
I would suggest to read through the code in micro-cors, it's very simple code, you can understand what it'll do in under few minutes, which makes me didn't concern about adding this into my dependency.
I was able to bypass this issue using micro-cors.
I checked its code and it doesn't differ that much of what I attempted to do myself by using res.setHeader
manually, probably missed something I guess.
Nevertheless I don't understand why the settings in now.json
were not working correctly and I need to perform this manually in the serverless function.
Anyways, in case someone else finds this post, I ended up with something like this:
import micro from "micro-cors";
function MyApi(req, res) {
if (req.method === "OPTIONS") {
return res.status(200).end();
}
// handling other requests normally after this
}
const cors = micro();
export default cors(MyApi);
I'll probably will try again with a self-written solution in order to understand better what went wrong and also because I don't want an extra dependency.
Will update this answer if I do that.
Edit: After checking a bit deeper I found that another issue was the library express-jwt
specifically changing the res
object when the jwt
parse failed.
I had a small middleware that was breaking everything by doing:
await authValidateMiddleware(req, res);
When that await
failed, it broke everything down the line because express-jwt
changed the res
headers unknowingly (setting the error) and then I tried to set the res
headers manually trying to handle the error correctly myself, therefore throwing issues about "changing the res
headers more than once".