CORS issue: Next.js application - reactjs

I am trying to deploy a Next.js application with Dokku (Heroku). The application was previously deployed to Vercel without error, but on the Dokku deploy CORS is failing. I've made some progress in fixing it.
The Next.js server communicates with another eternal Python Django API through an API gateway.
Initially the POST request errored with the "No 'Access-Control-Allow-Origin' header is present" error. I added the headers to moduleExports:
next.config.js
const moduleExports = {
async headers() {
return [
{
source: "/api/(.*)",
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
{ key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
{ key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
]
}
]
},
async redirects() {
return [
{
source: '/account',
destination: '/account/profile',
permanent: true,
},
]
},
};
Thereafter I began receiving a new error that the preflight options request did not return 200. I added a check for options requests to my handler:
pages/api/sign-up.js
export default async function handler(req, res) {
if (req.method === 'OPTIONS') {
res.status(200).end();
}
const { email, password1, password2, first_name, last_name } = await req.body
const response = await fetch(REGISTRATION_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( { email, password1, password2, first_name, last_name } ),
});
const data = await response.json()
res.status(200).json(data)
}
At this point, interestingly, the request does make it to the gateway and is accepted and indeed the new user is successfully created at the Django API. However the communication between the next.js server and client still shows a CORS error and the page does not update to show success. The CORS error is back to the first one "No 'Access-Control-Allow-Origin' header is present on the requested resource". The difference of course is that earlier the user was not created on the Django end.
My question of course is how can I fix this since I'm out of ideas or things to try now.

There is your problem:
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
// ...
]
You cannot use the wildcard (*) in conjunction with credentialed requests. As explained in the section entitled Credentialed requests and wildcards of the MDN Web Docs about CORS:
When responding to a credentialed request, the server must not specify the "*" wildcard for the Access-Control-Allow-Origin response-header value, but must instead specify an explicit origin; for example: Access-Control-Allow-Origin: https://example.com.
Accordingly, instead of using the wildcard, you should specify the allowed origin(s) explicitly:
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "https://yourfrontendorigin.com" },
// ...
]

Related

CORS policy: Request header field token is not allowed by Access-Control-Allow-Headers

I'm making a GET type request to an api that requests two authentication headers, one of them is called token and the other Authentication.
I'm passing the two required parameters, as in the printscreen below:
In return, I get the error:
Access to XMLHttpRequest at 'https://pay.apiname.com.br/v1/sellers/d.....2/payments/list' from origin 'http://10.0.0.150:3006' has been blocked by CORS policy: Request header field token is not allowed by Access-Control-Allow-Headers in preflight response.
I'm using axios to make requests. In the file below you have the configuration of the request for this specific middleware. I run this request even in redux action when necessary.
paymentApi: {
client: axios.create({
baseURL: process.env.API,
responseType: 'json',
}),
options: {
returnRejectedPromiseOnError: true,
interceptors: {
request: [
({ getState }, config) => {
// here I look for the user information from the state, where it contains the authorization and token needed for the header.
const { auth } = getState().get('login').toJS();
return {
...config,
headers: {
...(config.headers || {}),
token: auth.user.token_parcela
? `${auth.user.token_parcela}`
: undefined,
Authorization: auth.user.api_key_parcela
? `${auth.user.api_key_parcela}`
: undefined,
Accept: 'application/json',
'Content-Type': 'application/json',
},
};
},
],
response: [
{
success: (store, response) => response,
error: (_store, error) => {
console.error(error);
return Promise.reject(error);
},
},
],
},
},
},
Strange thing is that if you try to make this request using postman, I can do it without any problems, see the printscreen below.
The issue is not related to your code, the API where it is hosted/built upon needs to allow CORS origin, and you will have to specify your website from which you are calling this API as the origin.
Read more about CORS here - CORS
Also it works in Postman is because the API is called directly and not from any source/origin/website unlike your code.

How can I add a CORS header to this Lambda function

Please bare with me as this is my first stack overflow post, but I have minimal backend experience and am really struggling to meet CORS requirements.
I want to use AWS (SES, API Gateway, Lambda) to send form data to our company email account. My function works currently when testing in AWS, but it doesn't work on the client side of my site. From what I've gathered from research so far, my Lambda function needs a CORS header to work. Here is the code:
var aws = require("aws-sdk");
var ses = new aws.SES({ region: "us-east-1" });
exports.handler = async function(payload) {
var params = {
Destination: {
ToAddresses: ['placeholder#place.com'],
},
Message: {
Body: {
Text: {
Data: `\n
${payload.fullName} has tried to contact you. \n
Message: \n
-------------------- \n
${payload.comments} \n
-------------------- \n
Here is the sender's contact information: \n
Name: ${payload.fullName} \n
Email: ${payload.emailAddress} \n
Phone: ${payload.phone} \n
Company: ${payload.companyName}`
},
},
Subject: { Data: payload.subject },
},
Source: 'placeholder#place.com',
};
return ses.sendEmail(params).promise()
};
I'm looking at this code as an example of how to include a CORS header:
exports.handler = async (event) => {
let data = {};
let res = {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' // replace with hostname of frontend (CloudFront)
},
body: JSON.stringify(data)
};
return res;
};
Can anyone help me to combine these two approaches? I don't understand how to make the SES function into a more traditional response. I am mostly a frontend dev, so I expect that I'm missing something silly. I appreciate any responses though.
If you can change the API Gateway integration type to Lambda Proxy, then this code can help you.
Move the entire code in the handler method to another function say sendEmail
const sendEmail = async function(payload) {
// Your code to crete the `params` omitted for brevity
return ses.sendEmail(params).promise()
};
The handler can call this function and based on the outcome of this function send an appropriate result with the CORS headers
exports.handler = async function(event) {
const payload = JSON.parse(event.body);
const CORS_HEADERS = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' // Your origin name
};
try {
await sendEmail(payload);
return {
statusCode: 200,
headers: CORS_HEADERS,
body: '{}'
}
} catch(err) {
return {
statusCode: 500, // Can be 4XX or 5XX depending on the error
headers: CORS_HEADERS,
body: `{"err": ${err.messge}}`
}
}
}
For this to work for CORS requests, you also need to ensure the OPTIONS request responds with appropriate headers. You can do so using the AWS console following this documentation. For CloudFormation along with api-gateway V2, this documentation should help. For AWS SAM, this documentation should help (If you are not already using any Serverless development tool, take a look at AWS SAM).
If you don't wish to use the Lambda proxy, then ensure the integration response send the appropriate CORS headers for both the OPTIONS request and the POST request. This can help.

Netlify, Lambdas, Stripe, and Googlebots

Ive been working on an SPA with React that i have deployed on Netlify. The application uses stripe.js as a form of payment. While all of the functionality of stripe appears to be working fine on the user side, we are running into a problem with Google Search Console. It seems that the Googlebot Crawler is being blocked by the Stripe robots.txt file. basically the ultimate goal is to be approved for google adsense and after numerous rejections (even with prerendering and a lot more content added) we are still getting rejected. when we tried the google search console to see what google bot crawlers see we have absolutely no errors, the site is mobile friendly BUT we are getting this error that is shown below. While I obviously don't have any control over their use of Stripes robot.txt file, the Search console is also telling me this:
Message:
Access to XMLHttpRequest at 'https://m.stripe.com/6' from origin 'https://m.stripe.network' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Source:
https://m.stripe.network/inner.html#url=https%3A%2F%2Fwww.stateiqtest.org%2F&title=&referrer=&muid=NA&sid=NA&version=6&preview=false:0
The last thing i want to mention is all of the stripe functionality is accessed and called from a serverless Lambda function through netlify... I am confused why the CORS issue is a policy for the bot but not for users of the site? I am attaching my stripe lambda function call which I just enabled the cors policy for... but once again ... why do i have to even do this? if im not getting errors in the console from the user side how come the bot can't access it? i have tried everything from changing my netlify.toml file to adding a robots.txt file which disallows the provided Stripe URL. any leads? let me know! your help is already appreciated ! :)
//client sides
import {loadStripe} from "#stripe/stripe-js"
export async function handleFormSubmission(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
sku: form.get('sku'),
quantity: Number(form.get('quantity')),
};
const response = await fetch('/.netlify/functions/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "GET, POST",
},
body: JSON.stringify(data),
}).then((res) => res.json())
const stripe=await loadStripe(response.publishableKey);
const {err}=await stripe.redirectToCheckout({
sessionId:response.sessionId
})
if(err){
console.log(err)
}
}
im honestly going to admit i haven't used stripe in a while and haven't had issues until now thus am revisiting code. here is another function that i believe makes the request and creates the stripe checkout...
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const inventory = require('./data/products.json');
exports.handler = async (event) => {
const { sku, quantity } = JSON.parse(event.body);
const product = inventory.find((p) => p.sku === sku);
const validatedQuantity = quantity > 0 && quantity < 2 ? quantity : 1;
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'required',
success_url: `${process.env.URL}/success`,
cancel_url: process.env.URL,
line_items: [
{
name: 'Cognitive Analysis',
currency:'USD',
amount: 299,
quantity: 1
},
],
});
return {
statusCode: 200,
body: JSON.stringify({
sessionId: session.id,
publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
}),
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "GET, POST",
}
};
};

CORS: proxying localhost via webpack-dev-server to external API

I'm building simple react-powered contact page and it would be swell if I could test email sending from my local development machine. I'm using Zoho Email API, which I tested with curl and Insomnia - there everything is fine, I'm able to send emails.
Problems start when I try to do the same from React app via axios. The app itself is served locally via webpack-dev-server. Without proxying request through wds, the browser complains about CORS. But when I'm trying to proxy request via wds with the appropriate headers set I'm being greeted with 400 Bad Request and not much more to aid with the debugging.
The relevant wds setup:
const ReactRefreshWebpackPlugin = require('#pmmmwh/react-refresh-webpack-plugin')
exports.devServer = ({ host, port } = {}) => {
const plugin = new ReactRefreshWebpackPlugin()
return {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers':
'X-Requested-With, content-type, Authorization',
},
proxy: {
'/api/accounts/ZOHO_ID_NOT_SHOWN/messages': {
target: 'https://mail.zoho.eu',
secure: false,
},
},
https: true,
contentBase: path.join(__dirname, 'assets'),
stats: 'errors-only',
historyApiFallback: true,
overlay: true,
hot: true,
host,
port,
},
plugins: [plugin],
}
}
And function in which I'm using to axios to send the email:
const sendEmail = async () => {
const { name, email, message } = formState
try {
const response = await axios({
method: 'post',
url: EMAIL_SEND,
headers: { Authorization: EMAIL_AUTH },
data: {
toAddress: EMAIL_TO,
subject: `Message from ${name} # ${email}`,
content: message,
},
})
console.log(response) // debugging
} catch (e) {
console.log(e.response)
}
}
In the above function, EMAIL_SEND is imported from elsewhere and its value is /api/accounts/ZOHO_ID_NOT_SHOWN/messages - mirroring the wds proxy value.
With the above setup, when I try to send a test email from localhost I'm getting 400 Bad Request... and not much more. I'm sure that the issue is with how I'm proxying the request via wds but I kind of run out of ideas how I can fix it. Any suggestions will be greatly appreciated!

After preflight (cors) request server change origin to * and chrome not display request (but i look response body). How to solve a problem?

After preflight (cors) request server change origin to * and chrome not display request (but i look response body).
Request headers
Chrome's error:
Access to fetch at 'http://localhost:6529/graphql' from origin 'http://localhost:3000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
I use express, cors, graphql, apollo on backend and react on frontend.
Cors configuration (backend):
app.use(cors({
origin: 'http://localhost:3000',
credentials: true,
maxAge: 86400,
optionsSuccessStatus: 200,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'].join(','),
}));
Headers configuration (frontend)
const credentials = "include";
let client: ApolloClient<NormalizedCacheObject> | null = null;
export function createClient(cookie: any, ctx: any, store: any): ApolloClient<NormalizedCacheObject> {
storage.setItem("ctx", ctx);
client = new ApolloClient({
cache,
link: ApolloLink.from([
onError(({graphQLErrors, networkError}) => {
if (graphQLErrors) {
if (!SERVER) {
const redirectUrl = getRedirect(graphQLErrors);
if (redirectUrl) {
location.assign(redirectUrl);
}
}
graphQLErrors.map(({message, locations, path}) => {
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
);
});
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
}),
new ReduxLink(store),
new BatchHttpLink({
credentials,
uri: GRAPHQL,
headers: cookie,
fetchOptions: {
withCredentials: true,
credentials,
},
}),
],
),
ssrMode: SERVER,
connectToDevTools: true,
});
return client;
}
How to solve a problem?
I just went through the problem myself and it was a nightmare. The reason it is not working is that the CORS header must have a . in it. http://localhost:3000 doesn't meet that qualification.
I solved this issue by going into my host's file (on a Mac: /etc/hosts) and redirecting a dummy domain such as api.myapp.local to 127.0.0.1 (localhost). Then I redirected my frontend to app.myapp.local. So now when the CORS request is made it is from http://app.myapp.local:3000 to http://api.myapp.local:3001 and it meets that requirement. You can call the domain whatever you would like.

Resources