AWS Lambda Set-Cookie header not setting in the browser - aws-lambda

I'm trying to set a cookie in my aws lambda function response. I don't have any header mapping as I'm using lambda proxy integration with API Gateway. The response code looks like this in the lambda function:
exports.handler = async (event) => {
const response = {
statusCode: 200,
"multiValueHeaders": {
"Set-Cookie": ["gtgm=6c7729687d5ff1a05f1a5dfb15ce3b8fa3f2b590; path=/; expires=Fri, 13-Feb-2032 13:27:44 GMT; secure; HttpOnly; SameSite=None"]
},
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Credentials": true,
},
};
return response;
};
I use fiddler to check the response and I can see the "Set-Cookie..." in the response which leads me to believe that the code above is correct? The issue is that the browser just ignores it and doesn't set any cookies at all except for the AWS DNT cookie. I'm not sure what else to check or if I've missed anything in the cookie config.
This is what my request looks like:
<Button
onClick={() => {
fetch(
"https:mysupercoolapi.com/cookie-test/",
{
// credentials: "include",
headers: {
"Content-Type": "application/json",
// "Access-Control-Allow-Credentials": "true",
},
}
)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}}
>
Cookie Test
</Button>
Not sure what I'm missing or where I'm going wrong.

I magaed to figure this one out myself. The setup above was fine but the browser requires you to have the following set properly in order for it to actually set the cookie:
In the request: Set "credentials" as include. Depending on what library you're using (fetch would be credentials: "include"). You can see it is commented out in my original post above which is not correct.
In the Response header in the Lambda function you need to set you origin e.g., "Access-Control-Allow-Origin": "http://localhost:3002". After testing I switched it to my actual domain. If there's a way to keep this set to the actual domain but still have it work while testing on localhost please let me know.
In API Gateway you need to set the CORS values as in the screenshot below to ensure the preflight (OPTINOS) call is made correctly as well.
The key is to have both the request and response headers configured correctly. This is harder to do when using something like AWS but much easier with something like express.js where you can use the middleware.

Related

Access to fetch at '' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

I have this api (method get) that is connected to a lambda function that does a simple select from a database, if i test the endpoint with postman with a null body it does work (if i understood, postman is not under the same CORS policy), as well as typing the endpoint on the browser.
But when i try to do a fetch from a simple js, i get the error :
Access to fetch at '...' from origin 'http://localhost' 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.
I enabled CORS in API Gateway, both with the Enable CORS option
and with the Enable API Gateway CORS when creating a new resource
If i test my endpoint with gateway, i also get that Allow-content-allow-origin : * is in my response header :
What should i do to fix this problem?
here is the JS fetch :
console.log("pre fetch");
Show();
console.log("post fetch");
function Show(){
fetch("...").then(onResponse);//.then(onJson);
}
function onResponse(response){
console.log(response);
return response.json();
}
I removed the onJson to avoid confusion, but even with that in its the same problem.
Try to include that in your function too, like this,
I hope this would work:
const headers = {'Content-Type':'application/json',
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods':'POST,PATCH,OPTIONS'}
const response = {
statusCode: 200,
headers:headers,
body: JSON.stringify(X),
};
return response;
Here X is the response that you want to return.
If you are using Node.js you needs to install cors.
npm install cors.
After installing cors, include it in the page where you are using fetch function as shown below;
const cors = require('cors');
app.use(cors());
and the error will be solved.
I made a video on how to fix this.
You need to go into the Lambda function and add special code:
original (does NOT work):
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
new one, that works:
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
You can find this solution in here: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
Only you need to replace the:
"Access-Control-Allow-Origin": "https://www.example.com",
with
"Access-Control-Allow-Origin": "*",
Special thanks to user, KnowledgeGainer
ALSO, you need to enable CORS on Gateway API side, just follow instruction from here: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors-console.html

API Gateway, blocked by CORS policy: No 'Access-Control-Allow-Origin' header

I know this question might be duplicated, but none of the existing question point to anything I'm not doing...
I've deployed an API using the serverless framework, but I'm having trouble with CORS.
I'm doing a get request using axios:
axios.get('https://test.execute-api.us-west-1.amazonaws.com/dev/test?from=2012-01-09T21:40:00Z')
.then(response => {
this.data = response.data;
})
.catch(error => console.log(error))
And I'm getting the following error:
Access to XMLHttpRequest at 'https://test.execute-api.us-west-1.amazonaws.com/dev/test?from=2012-01-09T21:40:00Z' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I've already done:
Made sure there's an OPTIONS method in API Gateway with a method response that looks like this:
Made sure I deployed those changes.
Also, the response of my Lambda function is returning the following headers:
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
"Access-Control-Allow-Origin": "http://localhost:8080",
"Access-Control-Allow-Credentials": "true",
},
Body: string(jsonEvents),
}, nil
I also tried setting Access-Control-Allow-Origin to '*'
My serverless.yml file has cors: true on each of the function events:
functions:
deploymentFrequency:
handler: bin/update/deployment-frequency
events:
- http:
path: deployment-frequency
method: post
cors: true
fetchDeploymentFrequency:
handler: bin/fetch/deployment-frequency
events:
- http:
path: deployment-frequency
method: get
cors: true
What am I missing? Nothing seems to work. The request works fine from Postman and it looks to be including the headers, so this seems to be an issue with the OPTIONS method.
My configuration is:
(event, context, callback) => {
callback(null, {
statusCode: (code || 200),
body: JSON.stringify(resp),
headers: { 'Access-Control-Allow-Origin': '*'},
});
}
and it works fine for me. I use to have the same issue as you before, but as long as you define your function with CORS: true and your response contains the header, you should be fine.
Note: Im didnt understand the sintax "map[string]string" and credentials should not be necessary at this case.
It turns out I was ignoring the status code from the response :(
I realized I was actually getting two errors:
A 406 status code for a missing Content-Type header
The CORS error
The first error was caused because I wasn't passing the Content-Type header to the request (I had a check in my code I completely forget that expects that header).
The second error was caused because I didn't add the Access-Control-Allow-Origin header to the error response of my function.
Enable Lamba proxy integration
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
},
Body: string(jsonEvents),
}, nil
In your terminal, go to the root project path and run:
npm i cors
And, after you need put this code in your index.js:
const cors = require("cors");
app.use(cors());

Chrome Extension fetch function not sending cookies

I have a chrome extension that does an ajax call using the fetch function to my server running laravel.
manifest perfmissions
"permissions": [
"webRequest",
"webRequestBlocking",
"webNavigation",
"activeTab",
"tabs",
"cookies",
"<all_urls>"
],
fetch call
fetch(this.url, {
credentials: 'include',
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
body: encodeDataToURL(telemetry)
})
.then(function (data) {
console.log('Request succeeded with JSON response', data);
})
.catch(function (error) {
console.log('Request failed', error);
});
Cookies
siusession=eyJpdiI6InlRS2wyb1BCZnJSSGtUaXVRelV4M3c9PSIsInZhbHVlIjoiRThteUk4MmVxeXV6a1N5ZUxTaFpxcUtSazJQRE1ZUUNQUWlBREVTdHRQM2pjNEVJUVUxd3gwM1JZMDNjOXR2TyIsIm1hYyI6ImUwMGQyNmAAhwQ3YWQ4YzRhOWVhYTk2ZjI2NDgwNTljNDE2YWU5NTdlZWM1MThiZWJjYzI3NmZjZWRhOGRlMzIifQ%3D%3D; expires=Tue, 25-Sep-2018 04:28:56 GMT; Max-Age=28800; path=/; secure; httponly; samesite=lax
I have a opened session on my browser to that domain, which makes me having cookies with the Session ID and XSRF-TOKEN.
The problem is, it doesn't send the cookies with the call.
And on firefox, the same exact code and manifest it does send the browser cookies with the call.
What can be wrong? Does chrome require some sort of different permissions or another way to make the call including cookies?
Assuming the fetch call is made from the background script, you'll need to query the cookies and insert those in the http header.

Cookie is "undefined" in Koa/Express request

I'm using Koa JS framework for jwt authentication.
So basically when the user signs in, I set a jwt token (signed) to user's browser cookie, which seems to work fine as shown below (Chrome cookie settings):
(www.localhost.com instead of localhost is because I edited my hostfile, but this should have no effect in setting/getting cookies)
The problem, however, is when I send a POST request to my local Koa server, the jwt cookie is undefined. All I'm doing to verify the token is this:
routes.js
const Router = require("koa-router");
const router = new Router();
router.post(`api/authenticate`, function* () {
const jwt = this.cookies.get("jwt", { signed: true }); //jwt is undefined!!
if (!jwt)
this.throw("Invalid or expired token!");
this.status = 200;
});
//...
app.use(router.routes());
app.use(router.allowedMethods());
Here, this.cookies.get("jwt") returns undefined. The POST request is sent using AXIOS library with "withCredentials: true" header and a valid CSRF token:
authenticate.js
axios.post("api/authenticate", {}, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-Token": "A VALID CSRF TOKEN GENERATED BY SERVER",
"Content-Type": "application/json",
},
withCredentials: true,
});
Can anyone help me find out why this.cookies.get fails to fetch cookie from a simple POST request? I'm simply posting to my localhost, so I believe this is not a CORS problem.
What is more strange is that when I check my chrome developer tool, the "jwt" and "jwt.sig" tokens are successfully included in the request header..
Any help would be appreciated.
Update: Setting the cookie
//...
this.cookies.set("jwt", "SOME JWT GENERATED BY SERVER", {
httpOnly: true,
signed: true,
});
//...

CORS: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true

I have a setup involving
Frontend server (Node.js, domain: localhost:3000) <---> Backend (Django, Ajax, domain: localhost:8000)
Browser <-- webapp <-- Node.js (Serve the app)
Browser (webapp) --> Ajax --> Django(Serve ajax POST requests)
Now, my problem here is with CORS setup which the webapp uses to make Ajax calls to the backend server. In chrome, I keep getting
Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
doesn't work on firefox either.
My Node.js setup is:
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:8000/');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
};
And in Django I'm using this middleware along with this
The webapp makes requests as such:
$.ajax({
type: "POST",
url: 'http://localhost:8000/blah',
data: {},
xhrFields: {
withCredentials: true
},
crossDomain: true,
dataType: 'json',
success: successHandler
});
So, the request headers that the webapp sends looks like:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: 'GET,PUT,POST,DELETE'
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: csrftoken=***; sessionid="***"
And here's the response header:
Access-Control-Allow-Headers: Content-Type,*
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
Content-Type: application/json
Where am I going wrong?!
Edit 1: I've been using chrome --disable-web-security, but now want things to actually work.
Edit 2: Answer:
So, solution for me django-cors-headers config:
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000' # Here was the problem indeed and it has to be http://localhost:3000, not http://localhost:3000/
)
This is a part of security, you cannot do that. If you want to allow credentials then your Access-Control-Allow-Origin must not use *. You will have to specify the exact protocol + domain + port. For reference see these questions :
Access-Control-Allow-Origin wildcard subdomains, ports and protocols
Cross Origin Resource Sharing with Credentials
Besides * is too permissive and would defeat use of credentials. So set http://localhost:3000 or http://localhost:8000 as the allow origin header.
If you are using CORS middleware and you want to send withCredential boolean true, you can configure CORS like this:
var cors = require('cors');
app.use(cors({credentials: true, origin: 'http://localhost:3000'}));
Expanding on #Renaud idea, cors now provides a very easy way of doing this:
From cors official documentation found here:
"
origin: Configures the Access-Control-Allow-Origin CORS header.
Possible values:
Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS.
"
Hence we simply do the following:
const app = express();
const corsConfig = {
credentials: true,
origin: true,
};
app.use(cors(corsConfig));
Lastly I think it is worth mentioning that there are use cases where we would want to allow cross origin requests from anyone; for example, when building a public REST API.
try it:
const cors = require('cors')
const corsOptions = {
origin: 'http://localhost:4200',
credentials: true,
}
app.use(cors(corsOptions));
If you are using express you can use the cors package to allow CORS like so instead of writing your middleware;
var express = require('express')
, cors = require('cors')
, app = express();
app.use(cors());
app.get(function(req,res){
res.send('hello');
});
If you want to allow all origins and keep credentials true, this worked for me:
app.use(cors({
origin: function(origin, callback){
return callback(null, true);
},
optionsSuccessStatus: 200,
credentials: true
}));
This works for me in development but I can't advise that in production, it's just a different way of getting the job done that hasn't been mentioned yet but probably not the best. Anyway here goes:
You can get the origin from the request, then use that in the response header. Here's how it looks in express:
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', req.header('origin') );
next();
});
I don't know what that would look like with your python setup but that should be easy to translate.
(Edit) The previously recomended add-on is not available any longer, you may try this other one
For development purposes in Chrome, installing
this add on will get rid of that specific error:
Access to XMLHttpRequest at 'http://192.168.1.42:8080/sockjs-node/info?t=1546163388687'
from origin 'http://localhost:8080' 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.
After installing, make sure you add your url pattern to the Intercepted URLs by clicking on the AddOn's (CORS, green or red) icon and filling the appropriate textbox. An example URL pattern to add here that will work with http://localhost:8080 would be: *://*
Though we have many solutions regarding the cors origin, I think I may add some missing part. Generally using cors middlware in node.js serves maximum purpose like different http methods (get, post, put, delete).
But there are use cases like sending cookie response, we need to enable credentials as true inside the cors middleware Or we can't set cookie. Also there are use cases to give access to all the origin. in that case, we should use,
{credentials: true, origin: true}
For specific origin, we need to specify the origin name,
{credential: true, origin: "http://localhost:3000"}
For multiple origins,
{credential: true, origin: ["http://localhost:3000", "http://localhost:3001" ]}
In some cases we may need multiple origin to be allowed. One use case is allowing developers only. To have this dynamic whitelisting, we may use this kind of function
const whitelist = ['http://developer1.com', 'http://developer2.com']
const corsOptions = {
origin: (origin, callback) => {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error())
}
}
}
Had this problem with angular, using an auth interceptor to edit the header, before the request gets executed. We used an api-token for authentification, so i had credentials enabled. now, it seems it is not neccessary/allowed anymore
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
//withCredentials: true, //not needed anymore
setHeaders: {
'Content-Type' : 'application/json',
'API-TOKEN' : 'xxx'
},
});
return next.handle(req);
}
Besides that, there is no side effects right now.
CORS ERROR With NETLIFY and HEROKU
Actually, if none of the above solutions worked for you then you might wanna try this.
In my case, the backend was running on Heroku and the frontend was hosted on netlify.
in the .env file, of the frontend, the server_url was written as
REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com"
and in the backend, all my api calls where written as,
app.get('/login', (req, res, err) => {});
So, Only change you need to do is, add /api at the end of the routes,
so, frontend base url will look like,
REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com/api"
and backend apis should be written as,
app.get('/api/login', (req, res, err) => {})
This worked in my case, and I believe this problem is specifically related when the front end is hosted on netlify.

Resources