Enable Cross-Origin Requests with Next.js API Routes
Kunal Shah / March 25, 2021
3 min read
Using httpOnly
cookies adds a level of security to your application by authenticating clients without making the cookie or JWT readable via javascript on the client itself. These are particularly useful to authenticate resources in Next.js API Routes.
Here's how to set up fetch
in the browser to work with Next's API middlewares.
Fetch exposes an option to include credentials made to a resource, which attach server-side httpOnly
cookies attached to the domain.
We set the request up to include
credentials:
fetch("https://example.com/api/secure_resource", {
method: 'GET',
credentials:'include',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({data})
}
Now, we need to receive a specific set of headers from the server to pass the cors requirements. The documentation warns us
Access-Control-Allow-Origin is prohibited from using a wildcard for requests with credentials: 'include'. In such cases, the exact origin must be provided; even if you are using a CORS unblocker extension, the requests will still fail.
Consequently we configure CORS at the beginning of our API routes to preconfigure the correct headers.
First, we set up middlewares according to the documentation
function initMiddleware(middleware) {
return (req, res) =>
new Promise((resolve, reject) => {
middleware(req, res, (result) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
Then create an initCors
method
// top of the file
import Cors from "cors";
// Initialize the cors middleware
export const initCors = initMiddleware(
// You can read more about the available options here: https://github.com/expressjs/cors#configuration-options
Cors({
methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
origin: ["http://other-example.com", "http://localhost:3001"],
credentials: true,
})
);
Notice that we cannot set origin
to *
to allow requests from any domain when the request has credentials
set to include
. Moreover, we'll need to set credentials
to true
on the server response in order to set the Access-Control-Allow-Origin
header to true
, which is necessary for the preflight request from the browser to pass and allow the original request to be made.
Dynamic Origins
If you require a dynamic origin alongside credentials: include
, you can combine the two methods above and reflect the requests' origin
property from the preflight request's headers
export function runMiddleware(req, res) {
return new Promise((resolve, reject) => {
Cors({
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
origin: req.headers.origin,
credentials: true
})(req, res, (result) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}