I have a PHP Script which successfully returns some simple Headers as well as a set-cookie header if called directly in the browser (or by postman). I can read the response-headers like that from chrome devTools. But as soon as I call it by Axios, the set-cookie header doesn't show up and there's no cookie saved in the browser.
I tried diffrent things like changing the response-headers server-side and using "withCredentials: true" with axios, but nothing worked. I don't even get an error or any cors-related problems.
PHP:
header("Access-Control-Allow-Origin: http://localhost:8080");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST, GET");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
header("Access-Control-Max-Age: 99999999");
setcookie("TestCookie", "Testing", time() + 3600, "/", "localhost", 0);
die();
JS:
Vue.prototype.$http = axios.create({
baseURL: XYZ,
withCredentials: true
})
So my first question is why does the header appear when calling the php script directly? And how can I archive to get the header through axios too?
probably cookie is 'httpOnly', which means client side javascript can not read it.
Therefore it is not showing in chrome cookie section.
test the same request in mozilla, header will show up.
This may not apply to your situation, but I had the same problem using axios in this standalone nodejs script.
const config = {
url: 'https://remote.url/login',
method: 'post',
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
data: qs.stringify({
'email': username,
'password': pwd
})
}
axios(config).then(res => {
console.log(res.headers);
}).catch(err => {
console.log(err);
})
This returned http status 200 without set-cookie in the headers. With curl the header was correctly retrieved, but the status code was 302
After adding the following config options to axios:
maxRedirects: 0,
validateStatus: function (status) {
return status <= 302; // Reject only if the status code is greater than 302
},
I received the set-cookie in axios in the response.header.
{
server: 'nginx/1.16.1',
date: 'Fri, 27 Dec 2019 16:03:16 GMT',
'content-length': '74',
connection: 'close',
location: '/',
'set-cookie': [
'cookiename=xxxxxxxxxxxxxxxxxxxxx; path=/; expires=Sat, 26-Dec-2020 16:03:16 GMT'
],
status: '302',
'x-frame-options': 'DENY'
}
Without maxRedirects: 0 I received the html of the homepage of the remote url I used.
Without validateStatus I received the set-cookie header in the err.response.headers object.
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.
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 });
For me is working adding {withCredentials: true} like this:
axios
.post(url, {
foo: foo,
baz: baz,
}, {withCredentials: true})
.then(.............
Related
I have some problems with sending a POST request to my REST-API.
The problem is, when I send it from a react application, it shows me this error in the debug console of firefox.
The funny thing is, that it works perfectly fine when sending the request with postman.
This is the code i use to make the request:
let apiURL = API_URL_BASE + "/api/authenticate"
let requestBody = JSON.stringify(
{
"username": this.getEnteredLoginUsername(),
"password": this.getEnteredLoginPassword()
}
);
let headerData = new Headers();
headerData.append('Accept', '*');
headerData.append("Access-Control-Allow", "*");
headerData.append('Content-Type', 'application/json');
headerData.append('Access-Control-Allow-Origin', '*');
headerData.append("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
headerData.append("Access-Control-Allow-Headers", "*");
let requestOptions = {
method: 'POST',
mode: 'cors',
redirect: 'follow',
body: requestBody,
headers: headerData
}
this.setState({loadingData: true});
fetch(apiURL, requestOptions).then( response => {
let responseStatus = response.status;
response.json().then( responseJSON => {
});
});
I hope someone can help me with this.
This is the error shown by firefox console: Image
You do seem to have a correct request header from the client-side, i.e the browser, but your server that is hosting the API must also send a response to the client back indicating that it allows cross-origin requests, Otherwise browser would not proceed ahead with your request. Setting cors headers from the server would depend on what framework you're using for the backend. In fact you need to add those cors header you've added here to the server code.
A sample response header would look like this :
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: * (Note: * means this will allow all domains to request to your server)
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
For express, you can follow this link.
More on CORS here
I thought I had solved this issue before and was able to make succesful post request to my local hosted apache server with fetch, but today I tried to make a post and I've gotten 'cors preflight request fail' error again. I'm not sure why it was working before and why it's suddenly not working again.
this is my php file headers which worked 3 days ago -
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Access-Control-Allow-Headers,Content-Type,Access-Control-Allow-Methods,
Authorization, X-Requested-With');
and this is the fetch request I'm using on it -
fetch("http://menu.com/menu/api/post/create.php", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'title' : 'qweqweqw',
'body' : '1111w',
'author' : '2222r',
'category_id' : '2'
})
})
.then( (response) => {
console.log(response);
});
here is the Postman code that is working :
POST /menu/api/post/create.php HTTP/1.1
Host: menu.com
Content-Type: application/json
cache-control: no-cache
Postman-Token: 83a8e0d1-f45a-4184-92f6-f0be2f8fcf5f
{
"title" : "new new title",
"body" : "new neqwsadasssssssssseqweqwew jew",
"author" : "new newzxxxxxxxxxxxxwq author",
"category_id" : "1"
}------WebKitFormBoundary7MA4YWxkTrZu0gW--
I'd appreciate any help with this.
edit-
These are the cors errors :
Access to fetch at 'http://menu.com/menu/api/post/create.php' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
In my apache server error log I get this ' PHP Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: '' for column 'category_id' at row 1 in /home/orphe$' But I'm not sure why it's getting this for category_id since I copy/pasted the working request from postman and it still doesn't work.
There's a preflight request (with OPTIONS), but the server doesn't return HTTP 200 OK.
Try adding the following to create.php:
header("HTTP/1.1 200 OK");
If this doesn't work please also include the request and response headers.
Changing my fetch requests headers to headers: new Headers() fixed everything.
I built a API Gateway with Serverless and the first route I made with allowCredentials: true won't work.
clients-confirmation:
handler: clients.onConfirmation
events:
- http:
path: clients/on-confirmation
method: post
cors:
origin: 'https://840b1a6d.ngrok.io' # <-- Specify allowed origin
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
- Access-Control-Allow-Origin
- Access-Control-Allow-Credentials
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
allowCredentials: true
cacheControl: 'max-age=600, s-maxage=600, proxy-revalidate'
authorizer: aws_iam
The user is signed in and I use AWS-Amplify Auth.post to post to the route.
I tried to put my localhost on ngrok to have a better feel of a real environment, but it won't work.
My ResponseBuilder provide correct headers I think.
export default class ResponseBuilder {
static create(data, withCredentials = false) {
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': 'https://840b1a6d.ngrok.io',
'Access-Control-Allow-Credentials': withCredentials, // true
'Access-Control-Allow-Headers': 'access-control-allow-origin, Access-Control-Allow-Headers, Access-Control-Allow-Origin, Origin,Accept, Access-Control-Allow-Credentials'
},
body: JSON.stringify(data),
'isBase64Encoded': false,
};
}
}
I use it on every requests. I tried to put the ngrok origin and the wildcard, but it keeps telling me the same error.
Is there something I don't know about the Response of a lambda or a setting that I should add to my serverless file?
Thank you very much for your help!
allowCredentials: true won't work with 'Access-Control-Allow-Origin': '*'.
You have to specify the origin explicitly:
'Access-Control-Allow-Origin': 'https://840b1a6d.ngrok.io'
also withCredentials in 'Access-Control-Allow-Credentials': withCredentials
should be true
When I use withCredentials I can see that cookies are sent to the server. But are send anything to the server. I use laravel as backend and angular 4 as frontend.
also, the cookie not shown on chrome developer tools and I can't access them using.
also, I used Cors middleware in laravel like:
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('Access-Control-Allow-Origin', 'http://localhost:4200');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
$response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Request-With');
$response->headers->set('Access-Control-Expose-Headers', 'Content-Disposition, x-total-count, x-filename');
$response->headers->set('Access-Control-Allow-Credentials', 'true');
return $response;
}
}
and when I request first time to the server for set cookie, these below response show:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Accept, Authorization, X-Request-With
Access-Control-Allow-Methods:GET, POST, OPTIONS, PUT, PATCH, DELETE
Access-Control-Allow-Origin:http://localhost:4200
Access-Control-Expose-Headers:Content-Disposition, x-total-count, x-filename
Cache-Control:no-cache, private
Connection:keep-alive
Content-Type:application/json
Date:Tue, 02 Jan 2018 09:04:32 GMT
Server:nginx/1.11.9
Set-Cookie:refreshToken=def502005a2ccf60eafdee2137df25a628d49f70c4571bedde628d50e45c0fe4b73f84f86bb469f0f77247dc2abc13c0c5c938027beb13fe534eb7bb41f4aed99faf49ebf2a238a007ce9514108951366db45a311d70d17d65dd48f5df6aa50f257c828cce16e589983c1e06c9e8d7d52806a1a9401569f87b3a394469e938c4ddbfcc7985e257d8f0d0df416e7b8a5bbd19e86050db3be5b90953c515934f529489f4e0ba62fb66ab883d1689349bbfb962332bceb322d978b7d20fa7e32bb94eb0050d8f94bfd3a780c4edfeea8eaa6954222b57c30229c2494fec38ee5292396400b25fadee04cad1729f9e9b9ccf12d21a6ed3f9663d41c5423536e64f83542df19fcede28247bfc12accf354e035182c3e019e4a2b55c807924cc50a12fa187b2f655fb19c1b42a1ae526763dd08dbd0d288b3a9c649216ab1abc60cd51bb97dfb0cb7b7b020ff270cf5c81bc94f2acd40f92edeefaf585d46d0750bf; expires=Sun, 25-Aug-2019 09:04:32 GMT; Max-Age=51840000; path=/; HttpOnly
Transfer-Encoding:chunked
X-RateLimit-Limit:60
X-RateLimit-Remaining:58
and when I request from frontend:
refreshToken(){
const headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.getToken()})
let options = new RequestOptions({ headers: headers, withCredentials: true });
return this.http.post(API_DOMAIN + 'refresh', JSON.stringify({}), options)
.map(
(response) => {
console.log(response);
return response.json()
},
)
.do(
tokenData =>{
localStorage.setItem('token', tokenData.access_token)
localStorage.setItem('expires_in', tokenData.expires_in)
}
)
}
chrome develope:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Accept, Authorization, X-Request-With
Access-Control-Allow-Methods:GET, POST, OPTIONS, PUT, PATCH, DELETE
Access-Control-Allow-Origin:http://localhost:4200
Access-Control-Expose-Headers:Content-Disposition, x-total-count, x-filename
Cache-Control:no-cache, private
Connection:keep-alive
Content-Type:application/json
Date:Tue, 02 Jan 2018 08:57:28 GMT
Server:nginx/1.11.9
Transfer-Encoding:chunked
X-RateLimit-Limit:60
X-RateLimit-Remaining:59
Front and back servers use different ports.
You can's save cookies because your development node.js server and your Laravel Vagrant box are on different domains. What you can do is to proxy calls from node.js to the the Laravel server.
Create proxy.conf.json in the root of Angular app. Put an object with routes inside:
{
"/api": {
"target": "http://mydomian.test/",
"secure": false
}
}
I just have /api defined here because all my backend URIs are inside api.php. All calls that look like http://localhost:4200/api/someitems/1 will be proxied to http://mydomian.test/api/someitems/1.
Then edit package.json and change the value of start inside scripts to ng serve --proxy-config proxy.conf.json. Now npm run start will start with proxy. The problem I had with this proxy is that it resolves your URL (http://mydomian.test/) to an IP. When it calls Laravel with just an IP the nginx server does not know what to do because it must receive a domain name. nginx allows multiple website on the same IP so it must receive a domain name or to have a default website to which it redirects all calls those have no domain name. In case this is still not fixed in Angular's proxy go to /etc/nginx/sites-available on the Laravel machine, you'll have a mydomian.test config file there. It has listen 80; line there, change it to listen 80 default;.
Now you can change the API_DOMAIN variable to http://localhost:4200 (or remove it all together) and disable the CORS.
SO my graphql api is at https://gpbaculio-tributeapp.herokuapp.com/graphql I configured the uploaded, headers like this:
const fetchQuery = (operation, variables) => {
return fetch('/graphql', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
query: operation.text,
variables,
}),
}).then(response => {
return response.json()
})
}
I have read from MDN.
For requests without credentials, the server may specify "*" as a
wildcard, thereby allowing any origin to access the resource.
So I am trying to publish the app in codepen, and this is my error:
Failed to load https://gpbaculio-tributeapp.herokuapp.com/graphql:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'https://s.codepen.io'
Why is it telling me it doesn't pass 'Access-Control-Allow-Origin' headers?
Is there something wrong with my headers config?
You are setting the header in your request (in the client). The Access-Control-Allow-Origin header needs to be set on the server-side, and when you make a request, the response should contain that header.
The reason behind this header is that not every webpage can query every third-party domain. Being able to set this header from the request would defeat that whole point.
Try setting cors options and Access-Control-Allow-Origin headers in server side.
const graphQLServer = express();
const corsOptions = {
origin(origin, callback) {
callback(null, true);
},
credentials: true
};
graphQLServer.use(cors(corsOptions));
var allowCrossDomain = 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', 'Content-Type');
next();
}
graphQLServer.use(allowCrossDomain);
This may help you
CORS specification states, that requests for resources are "preflighted" with HTTP OPTIONS request, and reply headers for that OPTIONS must contain header:
Access-Control-Allow-Origin: *
you might check it with curl:
$ curl -I -X OPTIONS https://gpbaculio-tributeapp.herokuapp.com/graphql
HTTP/1.1 405 Method Not Allowed
Server: Cowboy
Connection: keep-alive
X-Powered-By: Express
Allow: GET, POST
Content-Type: application/json; charset=utf-8
Content-Length: 97
Date: Sat, 23 Sep 2017 11:24:39 GMT
Via: 1.1 vegur
Add OPTION handler with needed header, so your server answers:
$ curl -I -X OPTIONS https://example.localhost/
HTTP/1.1 204 No Content
Server: nginx/1.4.7
Date: Sat, 23 Sep 2017 11:27:51 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range
Content-Type: text/plain; charset=utf-8
Content-Length: 0
The problem is the browser's cross-origin problem.
The Access-Control-Allow-Origin header should be return by the server's response, and the header means the origin domain that can access to the API.
The client's request often take a header Origin, it's value is the current host address, like, www.example.com.
The values of Access-Control-Allow-Origin must contain the value of Origin means that the origin can access this API service. And then the browser will continue the request. If not, the browser will cancel the request.
More infomation, refre to CORS https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS