Ionic CORS Error, But Server Has CORS Enabled - ionic4

I have an Ionic 4 app that uses a lambda API hosted on AWS. CORS is enabled on the API Gateway. The following snippet is from a curl request to the API.
< content-type: application/json
< content-length: 42
< date: Sat, 16 Feb 2019 02:19:25 GMT
< x-amzn-requestid: 47a5fcac-3191-11e9-af42-d387861aa6ad
< access-control-allow-origin: *
< access-control-allow-headers: Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token
< x-amz-apigw-id: VK7vFGc4oAMFTqg=
< access-control-allow-methods: POST,OPTIONS
This post discusses a few possible workarounds (change content type, etc.), but they don't work.
Changing the Content-Type header to text/plain or removing that header altogether makes no difference.
The following error is also presented on the Ionic console
Cross-Origin Read Blocking (CORB) blocked cross-origin response
https://mycoolapi.com/GetLegal with MIME type application/json.
See https://www.chromestatus.com/feature/5629709824032768 for more details.
The following is my service code.
getLegal(data: any) {
return new Promise((resolve, reject) => {
let httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
this.httpClient.post(this.apiUrl+'/GetLegal', JSON.stringify(data), {
headers: httpHeaders,
})
.subscribe(res => {
resolve(new LegalResponse(res));
}, (err) => {
console.log("Oops, there has been an error")
reject(err);
});
});
}
Help?

This ended up being a bug on the Amazon side. The curl snippet was from a GET method, which was sending the CORS headers. The POST method was not. After redeploying the API without changing anything, the GET method was no longer sending the CORS headers and the POST method was. The application is working, for now.

Related

get CORS problem when ty to get a token in keycloak with vuejs and axios

I trying to access one keycloak with axios in my vuejs app, but I receive the cors error, can someone help me please? (If I make a post from POSTMAN to my keycloak works fine)
I using this code:
const params = new URLSearchParams();
params.append("grant_type", "password");
params.append("client_id", "notas-front");
params.append("username", usuario.value);
params.append("password", password.value);
console.log(params);
const config = {
// withCredentials: true,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
};
axios.defaults.headers.common["Access-Control-Allow-Origin"] =
"http://localhost:8080";
axios
.post(
"http://localhost:8082/auth/realms/lumera/protocol/openid-connect/token",
params,
config
)
.then((response) => {
console.log(response);
});
and get this error:
but when I look the request I can't find the error:
the OPTIONS returns 200
but the POST dont
Postman doesn't care about Same Origin Policy, browser do. That's why your request is working in Postman but not in the browser.
Access-Control-Allow-Origin is a response header, you can't set it on the client request. And as you can see from the OPTIONS response headers your server is returning: Access-Control-Allow-Origin: http://localhost:8080
In a development environment the best way to solve this is setting a proxy in your vue configuration. Otherwise you should configure the server to allow requests from localhost:8080
Configure Web Origins properly in the Keycloak notas-front client config.

How to enable CORS allow-origin header in express server with swagger-ui

My API server doesn't change or overwrite the Access-Control-Allow-Origin header.
I have an Angular 2+ application with an Express/Swagger-UI API.
The Access-Control-Allow-Origin header in the API cannot be '*' because I use 'withCredentials' in my requests and it's trigger an error on the browser.
I've used 'cors' for setting the CORS headers. Like this:
'use strict';
var configuration = require('./configuration');
var SwaggerExpress = require('swagger-express-mw');
var app = require('express')();
var mongoose = require('mongoose');
var bluebird = require('bluebird');
var bodyParser = require('body-parser');
var jwt = require('jsonwebtoken');
var compression = require('compression');
var cors = require('cors');
app.use(bodyParser.json({ limit: '50mb' }));
app.use(compression());
SwaggerExpress.create(config, function(err, swaggerExpress) {
if (err) { throw err; }
swaggerExpress.register(app);
var port = process.env.PORT || 10010;
mongoose.Promise = bluebird;
mongoose.connect(appConfig.dbConection());
mongoose.connection.on('error', console.error.bind(console, 'connection error: '));
mongoose.set('debug', true);
mongoose.connection.once('open', function() {
app.listen(port);
})
});
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
const swaggerDocument = YAML.load('./api/swagger/swagger.yaml');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
var allowedOrigins = ['http://localhost:4200', 'http://127.0.0.1:10010'];
app.use(cors({
origin: (origin, callback) => {
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) === -1) {
return callback(new Error('The CORS policy for this site does not allow the specified Origin'), false);
}
return callback(null, true);
},
exposedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization', 'api_key', 'x-api-key'],
credentials: true
}));
The Access-Control-Allow-Origin should match the array of allowed hosts.
When I call the API from the browser, it first make an OPTIONS request, that look correct, like this:
Request URL: http://localhost:10010/v1/loginApp
Request Method: OPTIONS
Status Code: 204 No Content
Remote Address: [::1]:10010
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-api-key
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Access-Control-Allow-Origin: http://localhost:4200
Access-Control-Expose-Headers: Origin,X-Requested-With,Content-Type,Accept,Authorization,api_key,x-api-key
Connection: keep-alive
Content-Length: 0
Date: Thu, 04 Apr 2019 08:25:53 GMT
Vary: Origin, Access-Control-Request-Headers
X-Powered-By: Express
But then it makes the GET/POST request, and then it has the default headers again:
Request URL: http://localhost:10010/v1/loginApp
Request Method: GET
Status Code: 200 OK
Remote Address: [::1]:10010
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Origin,X-Requested-With,Content-Type,Accept,Authorization,api_key,x-api-key
Content-Length: 770
Content-Type: application/json; charset=utf-8
Date: Thu, 04 Apr 2019 08:25:53 GMT
ETag: W/"302-BwX5DjK/lUY+ueP6UZ9TgWOTw/s"
Vary: Origin
X-Powered-By: Express
I think something (probably swagger-UI) is overwriting my headers configuration, but I can't figure it out.
You need to register your CORS middleware before the Swagger GET/POST handlers.
The OPTIONS request is succeeding because Swagger doesn't register an OPTIONS handler, so the request reaches the middleware.
The GET/POST requests are failing before the Swagger handlers accept the request before it reaches the CORS middleware.
Move app.use(cors({... so it is before app.use('/api-docs', swaggerUi....
It seems as if swagger-express-mw comes with CORS support built-in, see swagger_controllers in your config/default.yaml. I had the same issue and fixed it by removing cors from there.
Like you, I suspect it's overwriting the Access-Control-Allow-Origin header but didn't have time to check it properly.

Does Axios support Set-Cookie? Is it possible to authenticate through Axios HTTP request?

I'm trying to authenticate express API back-end using Axios HTTP request call.
I was able to see 'Set-Cookie' in the response header, but cookie was not set. Is it possible to set cookies through Axios HTTP calls?
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 355
Content-Type: application/json; charset=utf-8
Date: Fri, 28 Sep 2018 05:59:01 GMT
ETag: W/"163-PAMc87SVHWkdimTJca7oRw"
Set-Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; Max-Age=3.6; Path=/; Expires=Fri, 28 Sep 2018 05:59:04 GMT; HttpOnly
X-Powered-By: Express
Try this out!
axios.get('your_url', {withCredentials: true}); //for GET
axios.post('your_url', data, {withCredentials: true}); //for POST
axios.put('your_url', data, {withCredentials: true}); //for PUT
axios.delete('your_url', data, {withCredentials: true}); //for DELETE
For more information on this from the axios docs:
"withCredentials indicates whether or not cross-site Access-Control requests should be made using credentials" - https://github.com/axios/axios
More detail on withCredentials:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
Yes you can set cookies by Axios. The cookies need to be passed into the headers object. You can send cookies in a get/post/put/delete/etc. request:
As suggested by Aaron:
axios.get('URL', {
withCredentials: true
});
axios.post('URL', data, {
withCredentials: true
});
axios.put('URL', data, {
withCredentials: true
});
axios.delete('URL', data, {
withCredentials: true
});
Or you may also try this:
axios.get(url, {
headers: {
Cookie: "cookie1=value; cookie2=value; cookie3=value;"
}
}).then(response => {
console.log(response);
});
In case anyone else faces the problem I've had,
Here's a repost of my answer on a similar question https://stackoverflow.com/a/62821342/8479303
In my case, the network panel showed that the response had the 'Set-Cookie' header, but in axios the header wouldn't show up, and the cookie was being set.
For me, the resolution was setting the Access-Control-Expose-Headers header.
For explanation, from this comment on an issue in the axios repository I was directed to this person's notes which led me to set the Access-Control-Expose-Headers header -- and now the cookie is properly setting in the client.
So, in Express.js, I had to add the exposedHeaders option to my cors middleware:
const corsOptions = {
//To allow requests from client
origin: [
"http://localhost:3001",
"http://127.0.0.1",
"http://104.142.122.231",
],
credentials: true,
exposedHeaders: ["set-cookie"],
};
...
app.use("/", cors(corsOptions), router);
It was also important that on the axios side I use the withCredentials config in following axios requests that I wanted to include the cookies.
ex/
const { data } = await api.get("/workouts", { withCredentials: true });
I tried setting withCredentials: true but was still getting this error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/users/register. (Reason: CORS request did not succeed).
CORS was configured to allow requests from the frontend port.
I had to change the default options for axios like so:
axios.defaults.withCredentials = true
And the issue was solved. No error and Set-Cookie working as expected.
cookie can't be touched, the thing is it gets bundled to request object after appended to the response object.
function sign(req,res){
res.cookie("x-token", signed, { maxAge: (new JWTService().jwtExpirySeconds *
1000) });
}
client after receiving this response just have to continue with requests, set-cookie in the name of "Cookie " will be bundled to those request, like this
caveat: when http cookie expires its is automatically removed and not bundled to request there after.

Method DELETE is not allowed even though Access-Control-Allow-Methods is set to allow it

My middleware code:
app.use(cors());
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
if (req.method === 'OPTIONS') {
res.send(200);
} else {
next();
}
});
Every time that I've found someone with the same error message, the response that rectifies their problem involves telling them to set "Access-Control-Allow-Methods" just like I have it.
My delete method on the front end:
deleteSaved(url) {
// return axios.delete("https://api.mlab.com/api/1/databases/etc.", {
// params: {
// "url": url
// }
// })
// .then(function(results) {
// console.log("axios results", results);
// return results;
// });
return fetch("/saved", {
method: 'DELETE',
body: JSON.stringify({url: url}),
mode: 'cors',
cache: 'default'
}).then(function(response) {
return response;
}).then(function(data){
console.log(data);
return data;
});
}
I have both fetch and axios there because I thought I was having trouble with the axios delete method. In the development server, both calls work properly.
In the axios method, I have my db API URL for the sake of simplicity (to make it easier to get things to work); in the fetch method, I have '/saved' because when I asked this question last time, someone commented and suggested that I proxy all DB requests through my express server rather than having the browser connect directly. I tried a few things (setting "proxy" and "https_proxy" to my mlab api url in the package.json associated with the create-react-app front end), but I don't think I really understand what that means. The proxy didn't work (the "/saved" would result in an error for a GET request at the apps URL + /saved rather than for the mlab API url) and I simply can't find anything on google about proxying requests to the DB through the express server.
My response headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Methods:POST,GET,OPTIONS,PUT
Access-Control-Allow-Origin:https://nyt-mern-app.herokuapp.com
Access-Control-Max-Age:1728000
Allow:POST,GET,OPTIONS,PUT
Cache-Control:no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0
Connection:Keep-Alive
Content-Length:0
Content-Type:text/html;charset=UTF-8
Date:Thu, 26 Oct 2017 19:34:08 GMT
Expires:Tue, 01 Feb 2000 08:00:00 GMT
Keep-Alive:timeout=5, max=99
Last-Modified:Thu, 26 Oct 2017 19:34:08 GMT
Pragma:no-cache
Server:Apache/2.4.7 (Ubuntu)
X-Frame-Options:DENY
Like I said, everything works in a local development environment. How do I get this to work in deployment?

Magento 2 API with Angular 2 Token authentication

This is integration issue. Your help is much appreciated (Hint || Guide)
I have both Angular2 and Magento2 (bitnami) installed locally. Magento conf was changed to have the right headers (See below) for CROS.
I'm calling Magento2 from Angular2 to get the token and I'm getting the following issue:
OPTIONS http://192.168.56.1:82/magento/index.php/rest/V1/integration/admin/token 400 (Bad Request)
XMLHttpRequest cannot load http://192.168.56.1:82/magento/index.php/rest/V1/integration/admin/token. Response for preflight has invalid HTTP status code 400
EXCEPTION: Response with status: 0 for URL: null
Angular 2 side:
let headers = new Headers({'Content-type': 'application/json'});
headers.append('Access-Control-Allow-Origin', '*');
headers.append('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,PUT,DELETE');
headers.append('Access-Control-Allow-Headers', 'Origin,Authorization,X-Auth-Token,Accept,Content-Type');
headers.append('Access-Control-Allow-Credentials', 'true');
let options = new RequestOptions({ headers: headers });
return this.http.post( 'http://192.168.56.1:82/magento/index.php/rest/V1/integration/admin/token',
JSON.stringify('{"username":"angUser", "password":"angUser2017"}'),
options)
.map(res => res.json());
Magento2 API User
angUser / angUser2017
Consumer Key: 5bhvi7gjvyafcp35rajuxh0y4me2plga
Consumer secret: yh1nefyw1u80rd0ip1q6f8pijv9x72f1
Access Token: g5plfwth2rhlwtuwfhhqp7mg6sebrxc3
Access Token Secret: i1f4t7j65oo8ydtnteub9xr7wrswe99c
Magento headers:
Response Headers
Access-Control-Allow-Credentials: True
Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization
Access-Control-Allow-Methods: GET,POST,OPTIONS,PUT,DELETE
Access-Control-Allow-Origin: *
I had a similar issue before and I tracked it down to this method where there is no check for ->isOptions(). So every API call from another domain was triggering a Request method is invalid exception.
/**
* Retrieve current HTTP method.
*
* #return string
* #throws \Magento\Framework\Exception\InputException
*/
public function getHttpMethod()
{
if (!$this->isGet() && !$this->isPost() && !$this->isPut() && !$this->isDelete()) {
throw new \Magento\Framework\Exception\InputException(new Phrase('Request method is invalid.'));
}
return $this->getMethod();
}
You can find a possible workaround in the github forum if you are using apache.
In my specific case what I ended up doing was serving both front-end and api from the same domain to avoid problems with CORS (I use nginx).
An example of the configuration needed for this can be something like:
location ~ ^/(index.php/)?rest {
try_files $uri $uri/ /index.php?$args;
}
location / {
root /var/www/angular/public/;
index index.html;
}