I'm trying to authenticate my users from my app hosted in Vercel in order to retrieve their data, however, my application needs to communicate with Heroku where my API is located.
I've been trying several techniques and all of them have not been helpful at all. In fact, I believe, the reason is because sharing cookies cross-domain is not supposed to be possible.
Moreover, according to the Internet, there's something called reverse proxy.
First thing first, this is what I have in my server.js
file:
// Trust proxies
app.enable('trust proxy');
app.use(cors());
// Cookie parser
app.use(cookieParser());
// Set custom headers before routes to allow cross-domain functions
app.use((req, res, next) => {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers','Origin,X-Requested-With,Content-Type, Accept');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
const server = app.listen(port, () => {
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${port}`.yellow.bold
);
});
This is supposed to let me handle headers prior to declaring my routes. Now the funny (keep reading) part comes with Next.js as this seems to be the issue.
My application uses two files:
helpers/
fetchurl.js
pages/
api/
proxy.js
The fetchurl is a fetch function that works in both the client and server sides and so far works great:
export const fetchurl = async (
url = ``,
method,
cache = 'default',
bodyData,
signal = undefined || null || {},
multipart = false,
isRemote = false,
token = null
) => {
// const myCookies = await cookies();
// const token = myCookies.get("xAuthToken");
let requestBody = null
let customHeaders = {
Authorization: token ? `Bearer ${token?.value}` : '',
'Content-Type': 'application/json',
credentials: 'include',
}
if (
bodyData &&
typeof bodyData === 'object' &&
!Array.isArray(bodyData) &&
bodyData !== null &&
!multipart
) {
// Check if bodyData is a plain object before stringifying
requestBody = JSON.stringify(bodyData)
}
if (multipart) {
const data = new FormData()
customHeaders[
'Content-Type'
] = `multipart/form-data; boundary=${data._boundary}`
}
// If no signal is provided, create a new AbortController signal
if (signal !== undefined && signal !== null && signal !== ``) {
const controller = new AbortController()
signal = controller.signal
}
const response = await fetch(
isRemote ? url : `${process.env.NEXT_PUBLIC_API_URL}${url}`,
{
method: method,
cache: cache,
body: method !== 'GET' && method !== 'HEAD' ? requestBody : null,
signal: signal,
headers: customHeaders,
}
)
.then(async (res) => {
if (!res.ok) {
// check if there was JSON
const contentType = res.headers.get('Content-Type')
if (contentType && contentType.includes('application/json')) {
// return a rejected Promise that includes the JSON
return res.json().then((json) => Promise.reject(json))
}
// no JSON, just throw an error
throw new Error('Something went horribly wrong