ses.sendmail() gives CORS error. 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'..credentials mode is 'include' - amazon-s3

I have been stuck on this for 4 days now. I really need some insights.
I have a serverless express app deployed on AWS. I am serving my frontend from S3 and backend from lambda. API gateway has proxy as shown in the serverless.yml below.
I have also used cloudfront to map my domain(https://my.domain.com.au) with the S3 bucket origin URL.
The normal GET POST PUT DELETE requests are working fine. But when I try to access any of the other AWS service from Lambda I get following CORS error.
Access to XMLHttpRequest at 'https://0cn0ej4t5w.execute-api.ap-southeast-2.amazonaws.com/prod/api/auth/reset-password' from origin 'https://my.domain.com.au' 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'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
My use case is to send a mail from my app for which I tried using.
ses.sendEmail(params).promise();
This gave me the same error. So i tried invoking it through lambda, same error. Now i am trying to push mail contents to S3 and send mail from lambda using trigger but this gave me the same error.
The issue doesn't seem to be on the code as its working perfect from local environment. However, i don't want to leave any stones unturned.
Since, my lambda is in a VPC i have used internet gateway and tried setting up the private link as well.
Serverless.yml
service: my-api
# plugins
plugins:
- serverless-webpack
- serverless-offline
- serverless-dotenv-plugin
# custom for secret inclusions
custom:
stage: ${opt:stage, self:provider.stage}
serverless-offline:
httpPort: 5000
webpack:
webpackConfig: ./webpack.config.js
includeModules: # enable auto-packing of external modules
forceInclude:
- mysql
- mysql2
- passport-jwt
- jsonwebtoken
- moment
- moment-timezone
- lodash
# provider
provider:
name: aws
runtime: nodejs12.x
# you can overwrite defaults here
stage: prod
region: ${env:AWS_REGION_APP}
timeout: 10
iamManagedPolicies:
- 'arn:aws:iam::777777777777777:policy/LambdaSESAccessPolicy'
vpc:
securityGroupIds:
- ${env:AWS_SUBNET_GROUP_ID}
subnetIds:
- ${env:AWS_SUBNET_ID1}
- ${env:AWS_SUBNET_ID2}
- ${env:AWS_SUBNET_ID3}
environment:
/// env variables (hidden)
iamRoleStatements:
- Effect: "Allow"
Action:
- s3:*
- ses:*
- lambda:*
Resource: '*'
# functions
functions:
app:
handler: server.handler
events:
- http:
path: /
method: ANY
- http:
path: /{proxy+}
method: ANY
cors:
origin: ${env:CORS_ORIGIN_URL}
allowCredentials: true
headers: 'Access-Control-Allow-Origin, Access-Control-Allow-Headers, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization'
method: ANY
# you can add CloudFormation resource templates here
resources:
# API Gateway Errors
- ${file(resources/api-gateway-errors.yml)}
# VPC Access for RDS
- ${file(resources/lambda-vpc-access.yml)}
I have configured response headers as well:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", process.env.CORS_ORIGIN_URL);
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Origin, Access-Control-Allow-Headers, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT,DELETE");
next();
});

I actually have the same exact error as you but I've figured it out.
I'll just paste my code since you didn't show what your lambda function looks like.
I also know its been two weeks... so hopefully this helps someone in the future.
CORS errors are server side, and I'm sure you are aware. The problem with AWS SES is you have to handle the lambda correctly or it'll give you a cors error even though you have the right headers.
First things first... I don't think you have OPTIONS method in your api gateway...although I'm not sure if ANY can work as a replacement.
Second here is my code:
I check which http method I'm getting then I respond based on that. I am receiving a post event and some details come in the body. You might want to change the finally block to something else. The OPTIONS is important for the CORS, it lets the browser know that its okay to send the POST request (or at least that's how I see it)
var ses = new AWS.SES();
var RECEIVER = 'receiver#gmail.com';
var SENDER = 'sender#gmail.com';
exports.handler = async(event) => {
let body;
let statusCode = '200';
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,DELETE,POST,PATCH,OPTIONS',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Headers': 'access-control-allow-credentials,access-control-allow-headers,access-control-allow-methods,Access-Control-Allow-Origin,authorization,content-type',
'Content-Type': 'application/json'
};
console.log(event);
try {
switch (event.httpMethod) {
case 'POST':
event = JSON.parse(event.body);
var params = {
Destination: {
ToAddresses: [
RECEIVER
]
},
Message: {
Body: {
Html: {
Data: html(event.name, event.phone, event.email, event.message), // 'Name: ' + event.name + '\nPhone: ' + event.phone + '\nEmail: ' + event.email + '\n\nMessage:\n' + event.message,
Charset: 'UTF-8'
}
},
Subject: {
Data: 'You Have a Message From ' + event.name,
Charset: 'UTF-8'
}
},
Source: SENDER
};
await ses.sendEmail(params).promise();
break;
case 'OPTIONS':
statusCode = '200';
body = "OK";
break;
default:
throw new Error(`Unsupported method "${event.httpMethod}"`);
}
}
catch (err) {
statusCode = '400';
body = err.message;
}
finally {
body = "{\"result\": \"Success\"}"
}
console.log({
statusCode,
body,
headers,
})
return {
statusCode,
body,
headers,
};
}

Related

CORS header ‘Access-Control-Allow-Origin’ missing on response in addon but not on request

I am creating a Firefox extension which posts some data to a database.
I made all parts in a modular fashion and am now combining everything piece by piece.
As such I know that my code to POST data to the database works.
Now here is the part that stumps me :
When I then add this code to my firefox extension
I get the following error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3003/timed_shot_create. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 400.
Now ofcourse CORS was nothing new and to be expected when dealing with Cross Origin Resource Sharing, it is even in the name.
But the reason why I am here is because this pertains only to the response of the POST request. The request itself is fine and allowed with the following piece of config in the server:
app.use(
cors({
//todo change to proper origin when live
origin: "moz-extension://d07f1e99-96a0-4934-8ff4-1ce222c06d0d",
method: ["GET", "POST"],
})
);
Which was later changed to:
app.use(
cors({
origin: "*",
method: ["GET", "POST"],
})
);
And then simplified even more to:
app.use(cors())
This is in Nodejs btw using cors middleware.
But none of this seems to work when it is used inside a firefox extension, as a local client page works as intended but as soon as I add this to a firefox extension I get a CORS error specifically pertaining to the reponse message.
The client side post (in the background script of the extension) is:
async function postTimedShot(post_options) {
const response = await fetch(post_endpoint, post_options);
//console.log(response);
const json_response = await response.json();
console.log(json_response);
}
let post_options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(response_data),
};
postTimedShot(post_options);
And the api looks like this:
app.post("/timed_shot_create", (req, res) => {
console.log("Received POST request");
const data = req.body;
console.log(data);
const timeStamp = data.time_stamp;
//TODO add screenshot and Description text maybe??
//const lastName = data.last_name
const queryString =
"INSERT INTO " + timed_shots_database + " (time_stamp) VALUES (?)";
getConnection().query(queryString, [timeStamp], (err, results, fields) => {
if (err) {
console.log("Failed to insert new user: " + err);
res.sendStatus(500);
return;
}
//Todo change this message when adding more data in body
//res.header("Access-Control-Allow-Origin", "moz-extension://d07f1e99-96a0-4934-8ff4-1ce222c06d0d");
res.json({
status: "Success!!!",
time_stamp: timeStamp,
});
console.log("Inserted a new user with id: ", results.insertId);
});
});
Furthermore, this extension is only for personal use and will work with a local server under my complete control so complications due to security or cloud usage that people want to mention are appreciated but not necessary (I think, I am a bit of novice).
I will be happy to clarify anything that is unclear, or change this post if necessary, but I think it is a unique question as far as I could see on SO. Additionally if I need to provide more of the codebase I will.
I will also update this post if I find out more about this problem.
Thank you for reading :3.
After reading about this post https://stackoverflow.com/a/53025865/5055963
on SO I found out that it had to do with the permissions in the manifest of the extension.
Adding this line: "://.localhost/*".
Solved the issue for me.

Google Strategy NestJS

I have this issue, I think I've explored all the internet for it, I'm trying to implement an authentification with passport-google-oauth20 in NestJS.
I got an event click from the front then it goes in the first part of the code
#UseGuards(GoogleAuthGuard)
public async loginGoogle(#Req() req: any) {
}
thens it goes ine the GoogleAuthGuard Part :
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_SECRET,
callbackURL: 'http://localhost:5000/redirect',
scope: ['email', 'profile'],
});
// super({ usernameField: 'token' });
}
But in the network console part of the browser I got a CORS error 402 after a redirect 302.
Request URL: http://localhost:3000/redirect
Request Method: GET
Status Code: 302 Found
Remote Address: 127.0.0.1:3000
Referrer Policy: strict-origin-when-cross-origin
Request URL: https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fredirect&scope=email%20profile&client_id=[MyID].apps.googleusercontent.com
Request Method: OPTIONS
Status Code: 405
Remote Address: 216.58.204.109:443
Referrer Policy: strict-origin-when-cross-origin
I already tried to enable all type of cors :
app.enableCors({
origin: ['http://localhost:5000', 'http://localhost:3000'],
allowedHeaders: ['content-type'],
credentials: true,
preflightContinue: false,
optionsSuccessStatus: 204,
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
// exposedHeaders: '*',
});
I already configured the redirection on googleapis to allow this one too. I'm out of solution if anyone could help on it, it would be really appreciated.
It feels like the Authentification is good but when it's time to redirect something goes wrong and I can't put headers in the redirect.

APIGateway CORS enabled and Lambda proxy integration enabled but still running into CORS issues

I have the following CDK code for my API Gateway
this.restAPI = new RestApi(this, name, {
cloudWatchRole: true,
domainName: {
domainName: props.recordName,
certificate: props.cert,
endpointType: EndpointType.REGIONAL,
securityPolicy: SecurityPolicy.TLS_1_2
},
defaultCorsPreflightOptions: {
allowOrigins: Cors.ALL_ORIGINS,
allowMethods: ['OPTIONS', 'POST', 'GET', 'DELETE', 'PUT', 'PATCH'],
allowHeaders: Cors.DEFAULT_HEADERS,
maxAge: Duration.seconds(86400)
}
});
which results in this
But I'm still running into this error:
/#/actors:1 Access to fetch at 'https://web-api.alpha.myapp.com/api/v1/actors?page=1' from origin 'https://alpha.myapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
But if you look at my Access-Control-Allow-Origin it's set to '*'. So I'm not sure what I'm missing.
And just to validate I do have Lambda Proxy integration turned on
Apparently my lambda that returns the GET content actually has to include the relevant CORS headers. It's not enough to just turn on CORS support on API Gateway.

Serverless function with authorizer arn provided returns 401

I am using serverless.
When I setup one of my functions as the following, which includes authorizer, on the client, I receive 401.
However when I remove it, there are no problems.
provider:
name: aws
runtime: nodejs8.10
region: eu-west-1
environment:
USER_POOL_ARN: "arn:aws:cognito-idp:eu-west-1:974280.....:userpool/eu-west-1........"
functions:
create:
handler: handlers/create.main
events:
- http:
path: create
method: post
cors: true
authorizer:
type: COGNITO_USER_POOLS
name: serviceBAuthFunc
arn: ${self:provider.environment.USER_POOL_ARN}
On the client, I expect a logged in user of the same user pool could get expected response. However it returns 401.
Any help is appreciated. Thanks.
After desperate hours spent, I have come up with the solution.
For anyone who comes across the same issue, here is a solution that worked for me.
Add integration: lambda after cors: true (though the order doesn't matter).
Below is just demonstrating that.
functions:
create:
handler: handlers/create.main
events:
- http:
path: create
method: post
cors: true
integration: lambda // this solves the problem
authorizer:
type: COGNITO_USER_POOLS
name: serviceBAuthFunc
arn: ${self:provider.environment.USER_POOL_ARN}
Send Authorization header with the value of Auth.currentSession().idToken.jwtToken while making the request.
Below is an example for sending headers using API of #aws-amplify/api and Auth of #aws-amplify/auth.
const currentSession = await Auth.currentSession()
await API.post(
'your-endpoint-name',
"/your-endpoint-path/..",
{
headers: {
'Authorization': currentSession.idToken.jwtToken
}
}
)

No 'Access-Control-Allow-Origin' header is present on the requested resource with CORS module enabled

I am trying to get CORS working on my AWS Lambda server using Serverless. Right now I have two sub-domains (api.site.co and app.site.co).
In my app.js file on Express, I have installed CORS and enabled it like such:
app.use(
cors({
origin: 'https://app.site.co',
optionsSuccessStatus: 200,
}),
);
app.options('*', cors());
And then using the Axios module in React, I make this call:
axios
.post('/users/register', user)
.then(res => history.push('/login'))
.catch((err) => {
dispatch({
type: GET_ERRORS,
error: err.response.data,
});
});
Also, in my app.js file for the client app:
axios.defaults.baseURL = 'https://api.site.co';
When submitting any request, I receive this error:
Access to XMLHttpRequest at 'https://api.site.co/users/register' from origin 'https://app.site.co' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
So, far I have also tried changing my CORS configuration to:
app.use(cors());
app.options('*', cors());
But I am still getting the same error.
Any suggestions on how to address this?
Looks like there is a typo:
https://app.site.co/users/register => https://api.site.co/users/register
You're making a request to https://api.site.co but your configuration specifies https://app.site.co
This was a problem with the AWS Lambda Serverless setup. I needed to add
functions:
app:
handler: app.handler
events:
- http:
path: /
method: any
cors:
origin: 'https://app.site.co'
- http:
path: '{proxy+}'
method: any
cors:
origin: 'https://app.site.co'
So that it would allow CORS requests when proxying to the Express middleware.